[perf_skottiewasm_lottieweb] Do 2 loops with 25 points each and then pick 25 from the middle

Doing this will avoid the unreliable frames at the start (setup related) and at the end (teardown related). Also:
* Increase timeouts in lottie-web-perf.js and skottie-wasm-perf.js since we are looping twice now.
* Fix bug where ret code was 0 if puppeteer test failed.

NoTry: true
Bug: skia:9237
Change-Id: I0933ecb87113b40c162712076c38fb7379b352d4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/226836
Reviewed-by: Joe Gregorio <jcgregorio@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Ravi Mistry <rmistry@google.com>
diff --git a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_perf.json b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_perf.json
index 1890091..476905a 100644
--- a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_perf.json
+++ b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_perf.json
@@ -147,7 +147,7 @@
     "cmd": [
       "python",
       "-u",
-      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n    'main_frame_aborted',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])\n\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      frame_max = max(frame_max, current_frame_duration)\n      frame_min = (min(frame_min, current_frame_duration)\n                   if frame_min else current_frame_duration)\n      frame_cumulative += current_frame_duration\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/total_frames\nprint 'For %d frames got: %s' % (total_frames, perf_results)\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
+      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(\n      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])\n\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\n# Will contain tuples of frame_ids and their duration.\ncompleted_frame_id_and_duration = []\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      completed_frame_id_and_duration.append(\n          (frame_id, current_frame_duration))\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\ntotal_completed_frames = len(completed_frame_id_and_duration)\nif total_completed_frames < 25:\n  raise Exception('Even with 2 loops found only %d frames' %\n                  total_completed_frames)\n\n# Get frame avg/min/max for the middle 25 frames.\nstart = (total_completed_frames - 25)/2\nprint 'Got %d total completed frames. Using start_index of %d.' % (\n    total_completed_frames, start)\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\nfor frame_id, duration in completed_frame_id_and_duration[start:start+25]:\n  frame_max = max(frame_max, duration)\n  frame_min = min(frame_min, duration) if frame_min else duration\n  frame_cumulative += duration\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/25\nprint 'For 25 frames got: %s' % perf_results\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
       "[CLEANUP]/g3_try_tmp_1/lottie1.json",
       "/path/to/tmp/json",
       "lottie-web"
@@ -177,20 +177,19 @@
       "@@@STEP_LOG_LINE@python.inline@erroneous_termination_statuses = [@@@",
       "@@@STEP_LOG_LINE@python.inline@    'replaced_by_new_reporter_at_same_stage',@@@",
       "@@@STEP_LOG_LINE@python.inline@    'did_not_produce_frame',@@@",
-      "@@@STEP_LOG_LINE@python.inline@    'main_frame_aborted',@@@",
       "@@@STEP_LOG_LINE@python.inline@]@@@",
       "@@@STEP_LOG_LINE@python.inline@accepted_termination_statuses = []@@@",
       "@@@STEP_LOG_LINE@python.inline@if renderer == 'skottie-wasm':@@@",
       "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@elif renderer == 'lottie-web':@@@",
-      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(@@@",
+      "@@@STEP_LOG_LINE@python.inline@      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@current_frame_duration = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@total_frames = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@frame_id_to_start_ts = {}@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Will contain tuples of frame_ids and their duration.@@@",
+      "@@@STEP_LOG_LINE@python.inline@completed_frame_id_and_duration = []@@@",
       "@@@STEP_LOG_LINE@python.inline@for trace in trace_json['traceEvents']:@@@",
       "@@@STEP_LOG_LINE@python.inline@  if 'PipelineReporter' in trace['name']:@@@",
       "@@@STEP_LOG_LINE@python.inline@    frame_id = trace['id']@@@",
@@ -204,10 +203,8 @@
       "@@@STEP_LOG_LINE@python.inline@        continue@@@",
       "@@@STEP_LOG_LINE@python.inline@      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]@@@",
       "@@@STEP_LOG_LINE@python.inline@      total_frames += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_max = max(frame_max, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_min = (min(frame_min, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@                   if frame_min else current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_cumulative += current_frame_duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@      completed_frame_id_and_duration.append(@@@",
+      "@@@STEP_LOG_LINE@python.inline@          (frame_id, current_frame_duration))@@@",
       "@@@STEP_LOG_LINE@python.inline@      # We are done with this frame_id so remove it from the dict.@@@",
       "@@@STEP_LOG_LINE@python.inline@      frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@      print '%d (%s with %s): %d' % (@@@",
@@ -221,11 +218,28 @@
       "@@@STEP_LOG_LINE@python.inline@            frame_id, args['termination_status'])@@@",
       "@@@STEP_LOG_LINE@python.inline@        frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@total_completed_frames = len(completed_frame_id_and_duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@if total_completed_frames < 25:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  raise Exception('Even with 2 loops found only %d frames' %@@@",
+      "@@@STEP_LOG_LINE@python.inline@                  total_completed_frames)@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Get frame avg/min/max for the middle 25 frames.@@@",
+      "@@@STEP_LOG_LINE@python.inline@start = (total_completed_frames - 25)/2@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'Got %d total completed frames. Using start_index of %d.' % (@@@",
+      "@@@STEP_LOG_LINE@python.inline@    total_completed_frames, start)@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@for frame_id, duration in completed_frame_id_and_duration[start:start+25]:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_max = max(frame_max, duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_min = min(frame_min, duration) if frame_min else duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_cumulative += duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results = {}@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_max_us'] = frame_max@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_min_us'] = frame_min@@@",
-      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/total_frames@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'For %d frames got: %s' % (total_frames, perf_results)@@@",
+      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/25@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'For 25 frames got: %s' % perf_results@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@# Write perf_results to the output json.@@@",
       "@@@STEP_LOG_LINE@python.inline@with open(output_json_file, 'w') as f:@@@",
@@ -254,7 +268,7 @@
     "cmd": [
       "python",
       "-u",
-      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n    'main_frame_aborted',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])\n\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      frame_max = max(frame_max, current_frame_duration)\n      frame_min = (min(frame_min, current_frame_duration)\n                   if frame_min else current_frame_duration)\n      frame_cumulative += current_frame_duration\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/total_frames\nprint 'For %d frames got: %s' % (total_frames, perf_results)\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
+      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(\n      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])\n\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\n# Will contain tuples of frame_ids and their duration.\ncompleted_frame_id_and_duration = []\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      completed_frame_id_and_duration.append(\n          (frame_id, current_frame_duration))\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\ntotal_completed_frames = len(completed_frame_id_and_duration)\nif total_completed_frames < 25:\n  raise Exception('Even with 2 loops found only %d frames' %\n                  total_completed_frames)\n\n# Get frame avg/min/max for the middle 25 frames.\nstart = (total_completed_frames - 25)/2\nprint 'Got %d total completed frames. Using start_index of %d.' % (\n    total_completed_frames, start)\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\nfor frame_id, duration in completed_frame_id_and_duration[start:start+25]:\n  frame_max = max(frame_max, duration)\n  frame_min = min(frame_min, duration) if frame_min else duration\n  frame_cumulative += duration\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/25\nprint 'For 25 frames got: %s' % perf_results\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
       "[CLEANUP]/g3_try_tmp_1/lottie2.json",
       "/path/to/tmp/json",
       "lottie-web"
@@ -284,20 +298,19 @@
       "@@@STEP_LOG_LINE@python.inline@erroneous_termination_statuses = [@@@",
       "@@@STEP_LOG_LINE@python.inline@    'replaced_by_new_reporter_at_same_stage',@@@",
       "@@@STEP_LOG_LINE@python.inline@    'did_not_produce_frame',@@@",
-      "@@@STEP_LOG_LINE@python.inline@    'main_frame_aborted',@@@",
       "@@@STEP_LOG_LINE@python.inline@]@@@",
       "@@@STEP_LOG_LINE@python.inline@accepted_termination_statuses = []@@@",
       "@@@STEP_LOG_LINE@python.inline@if renderer == 'skottie-wasm':@@@",
       "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@elif renderer == 'lottie-web':@@@",
-      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(@@@",
+      "@@@STEP_LOG_LINE@python.inline@      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@current_frame_duration = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@total_frames = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@frame_id_to_start_ts = {}@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Will contain tuples of frame_ids and their duration.@@@",
+      "@@@STEP_LOG_LINE@python.inline@completed_frame_id_and_duration = []@@@",
       "@@@STEP_LOG_LINE@python.inline@for trace in trace_json['traceEvents']:@@@",
       "@@@STEP_LOG_LINE@python.inline@  if 'PipelineReporter' in trace['name']:@@@",
       "@@@STEP_LOG_LINE@python.inline@    frame_id = trace['id']@@@",
@@ -311,10 +324,8 @@
       "@@@STEP_LOG_LINE@python.inline@        continue@@@",
       "@@@STEP_LOG_LINE@python.inline@      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]@@@",
       "@@@STEP_LOG_LINE@python.inline@      total_frames += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_max = max(frame_max, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_min = (min(frame_min, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@                   if frame_min else current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_cumulative += current_frame_duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@      completed_frame_id_and_duration.append(@@@",
+      "@@@STEP_LOG_LINE@python.inline@          (frame_id, current_frame_duration))@@@",
       "@@@STEP_LOG_LINE@python.inline@      # We are done with this frame_id so remove it from the dict.@@@",
       "@@@STEP_LOG_LINE@python.inline@      frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@      print '%d (%s with %s): %d' % (@@@",
@@ -328,11 +339,28 @@
       "@@@STEP_LOG_LINE@python.inline@            frame_id, args['termination_status'])@@@",
       "@@@STEP_LOG_LINE@python.inline@        frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@total_completed_frames = len(completed_frame_id_and_duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@if total_completed_frames < 25:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  raise Exception('Even with 2 loops found only %d frames' %@@@",
+      "@@@STEP_LOG_LINE@python.inline@                  total_completed_frames)@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Get frame avg/min/max for the middle 25 frames.@@@",
+      "@@@STEP_LOG_LINE@python.inline@start = (total_completed_frames - 25)/2@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'Got %d total completed frames. Using start_index of %d.' % (@@@",
+      "@@@STEP_LOG_LINE@python.inline@    total_completed_frames, start)@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@for frame_id, duration in completed_frame_id_and_duration[start:start+25]:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_max = max(frame_max, duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_min = min(frame_min, duration) if frame_min else duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_cumulative += duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results = {}@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_max_us'] = frame_max@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_min_us'] = frame_min@@@",
-      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/total_frames@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'For %d frames got: %s' % (total_frames, perf_results)@@@",
+      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/25@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'For 25 frames got: %s' % perf_results@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@# Write perf_results to the output json.@@@",
       "@@@STEP_LOG_LINE@python.inline@with open(output_json_file, 'w') as f:@@@",
@@ -361,7 +389,7 @@
     "cmd": [
       "python",
       "-u",
-      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n    'main_frame_aborted',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])\n\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      frame_max = max(frame_max, current_frame_duration)\n      frame_min = (min(frame_min, current_frame_duration)\n                   if frame_min else current_frame_duration)\n      frame_cumulative += current_frame_duration\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/total_frames\nprint 'For %d frames got: %s' % (total_frames, perf_results)\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
+      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(\n      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])\n\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\n# Will contain tuples of frame_ids and their duration.\ncompleted_frame_id_and_duration = []\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      completed_frame_id_and_duration.append(\n          (frame_id, current_frame_duration))\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\ntotal_completed_frames = len(completed_frame_id_and_duration)\nif total_completed_frames < 25:\n  raise Exception('Even with 2 loops found only %d frames' %\n                  total_completed_frames)\n\n# Get frame avg/min/max for the middle 25 frames.\nstart = (total_completed_frames - 25)/2\nprint 'Got %d total completed frames. Using start_index of %d.' % (\n    total_completed_frames, start)\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\nfor frame_id, duration in completed_frame_id_and_duration[start:start+25]:\n  frame_max = max(frame_max, duration)\n  frame_min = min(frame_min, duration) if frame_min else duration\n  frame_cumulative += duration\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/25\nprint 'For 25 frames got: %s' % perf_results\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
       "[CLEANUP]/g3_try_tmp_1/lottie3.json",
       "/path/to/tmp/json",
       "lottie-web"
@@ -391,20 +419,19 @@
       "@@@STEP_LOG_LINE@python.inline@erroneous_termination_statuses = [@@@",
       "@@@STEP_LOG_LINE@python.inline@    'replaced_by_new_reporter_at_same_stage',@@@",
       "@@@STEP_LOG_LINE@python.inline@    'did_not_produce_frame',@@@",
-      "@@@STEP_LOG_LINE@python.inline@    'main_frame_aborted',@@@",
       "@@@STEP_LOG_LINE@python.inline@]@@@",
       "@@@STEP_LOG_LINE@python.inline@accepted_termination_statuses = []@@@",
       "@@@STEP_LOG_LINE@python.inline@if renderer == 'skottie-wasm':@@@",
       "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@elif renderer == 'lottie-web':@@@",
-      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(@@@",
+      "@@@STEP_LOG_LINE@python.inline@      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@current_frame_duration = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@total_frames = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@frame_id_to_start_ts = {}@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Will contain tuples of frame_ids and their duration.@@@",
+      "@@@STEP_LOG_LINE@python.inline@completed_frame_id_and_duration = []@@@",
       "@@@STEP_LOG_LINE@python.inline@for trace in trace_json['traceEvents']:@@@",
       "@@@STEP_LOG_LINE@python.inline@  if 'PipelineReporter' in trace['name']:@@@",
       "@@@STEP_LOG_LINE@python.inline@    frame_id = trace['id']@@@",
@@ -418,10 +445,8 @@
       "@@@STEP_LOG_LINE@python.inline@        continue@@@",
       "@@@STEP_LOG_LINE@python.inline@      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]@@@",
       "@@@STEP_LOG_LINE@python.inline@      total_frames += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_max = max(frame_max, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_min = (min(frame_min, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@                   if frame_min else current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_cumulative += current_frame_duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@      completed_frame_id_and_duration.append(@@@",
+      "@@@STEP_LOG_LINE@python.inline@          (frame_id, current_frame_duration))@@@",
       "@@@STEP_LOG_LINE@python.inline@      # We are done with this frame_id so remove it from the dict.@@@",
       "@@@STEP_LOG_LINE@python.inline@      frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@      print '%d (%s with %s): %d' % (@@@",
@@ -435,11 +460,28 @@
       "@@@STEP_LOG_LINE@python.inline@            frame_id, args['termination_status'])@@@",
       "@@@STEP_LOG_LINE@python.inline@        frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@total_completed_frames = len(completed_frame_id_and_duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@if total_completed_frames < 25:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  raise Exception('Even with 2 loops found only %d frames' %@@@",
+      "@@@STEP_LOG_LINE@python.inline@                  total_completed_frames)@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Get frame avg/min/max for the middle 25 frames.@@@",
+      "@@@STEP_LOG_LINE@python.inline@start = (total_completed_frames - 25)/2@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'Got %d total completed frames. Using start_index of %d.' % (@@@",
+      "@@@STEP_LOG_LINE@python.inline@    total_completed_frames, start)@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@for frame_id, duration in completed_frame_id_and_duration[start:start+25]:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_max = max(frame_max, duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_min = min(frame_min, duration) if frame_min else duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_cumulative += duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results = {}@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_max_us'] = frame_max@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_min_us'] = frame_min@@@",
-      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/total_frames@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'For %d frames got: %s' % (total_frames, perf_results)@@@",
+      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/25@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'For 25 frames got: %s' % perf_results@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@# Write perf_results to the output json.@@@",
       "@@@STEP_LOG_LINE@python.inline@with open(output_json_file, 'w') as f:@@@",
diff --git a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_perf_trybot.json b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_perf_trybot.json
index d537bda..a920206 100644
--- a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_perf_trybot.json
+++ b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_perf_trybot.json
@@ -149,7 +149,7 @@
     "cmd": [
       "python",
       "-u",
-      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n    'main_frame_aborted',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])\n\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      frame_max = max(frame_max, current_frame_duration)\n      frame_min = (min(frame_min, current_frame_duration)\n                   if frame_min else current_frame_duration)\n      frame_cumulative += current_frame_duration\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/total_frames\nprint 'For %d frames got: %s' % (total_frames, perf_results)\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
+      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(\n      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])\n\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\n# Will contain tuples of frame_ids and their duration.\ncompleted_frame_id_and_duration = []\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      completed_frame_id_and_duration.append(\n          (frame_id, current_frame_duration))\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\ntotal_completed_frames = len(completed_frame_id_and_duration)\nif total_completed_frames < 25:\n  raise Exception('Even with 2 loops found only %d frames' %\n                  total_completed_frames)\n\n# Get frame avg/min/max for the middle 25 frames.\nstart = (total_completed_frames - 25)/2\nprint 'Got %d total completed frames. Using start_index of %d.' % (\n    total_completed_frames, start)\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\nfor frame_id, duration in completed_frame_id_and_duration[start:start+25]:\n  frame_max = max(frame_max, duration)\n  frame_min = min(frame_min, duration) if frame_min else duration\n  frame_cumulative += duration\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/25\nprint 'For 25 frames got: %s' % perf_results\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
       "[CLEANUP]/g3_try_tmp_1/lottie1.json",
       "/path/to/tmp/json",
       "lottie-web"
@@ -179,20 +179,19 @@
       "@@@STEP_LOG_LINE@python.inline@erroneous_termination_statuses = [@@@",
       "@@@STEP_LOG_LINE@python.inline@    'replaced_by_new_reporter_at_same_stage',@@@",
       "@@@STEP_LOG_LINE@python.inline@    'did_not_produce_frame',@@@",
-      "@@@STEP_LOG_LINE@python.inline@    'main_frame_aborted',@@@",
       "@@@STEP_LOG_LINE@python.inline@]@@@",
       "@@@STEP_LOG_LINE@python.inline@accepted_termination_statuses = []@@@",
       "@@@STEP_LOG_LINE@python.inline@if renderer == 'skottie-wasm':@@@",
       "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@elif renderer == 'lottie-web':@@@",
-      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(@@@",
+      "@@@STEP_LOG_LINE@python.inline@      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@current_frame_duration = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@total_frames = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@frame_id_to_start_ts = {}@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Will contain tuples of frame_ids and their duration.@@@",
+      "@@@STEP_LOG_LINE@python.inline@completed_frame_id_and_duration = []@@@",
       "@@@STEP_LOG_LINE@python.inline@for trace in trace_json['traceEvents']:@@@",
       "@@@STEP_LOG_LINE@python.inline@  if 'PipelineReporter' in trace['name']:@@@",
       "@@@STEP_LOG_LINE@python.inline@    frame_id = trace['id']@@@",
@@ -206,10 +205,8 @@
       "@@@STEP_LOG_LINE@python.inline@        continue@@@",
       "@@@STEP_LOG_LINE@python.inline@      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]@@@",
       "@@@STEP_LOG_LINE@python.inline@      total_frames += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_max = max(frame_max, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_min = (min(frame_min, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@                   if frame_min else current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_cumulative += current_frame_duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@      completed_frame_id_and_duration.append(@@@",
+      "@@@STEP_LOG_LINE@python.inline@          (frame_id, current_frame_duration))@@@",
       "@@@STEP_LOG_LINE@python.inline@      # We are done with this frame_id so remove it from the dict.@@@",
       "@@@STEP_LOG_LINE@python.inline@      frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@      print '%d (%s with %s): %d' % (@@@",
@@ -223,11 +220,28 @@
       "@@@STEP_LOG_LINE@python.inline@            frame_id, args['termination_status'])@@@",
       "@@@STEP_LOG_LINE@python.inline@        frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@total_completed_frames = len(completed_frame_id_and_duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@if total_completed_frames < 25:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  raise Exception('Even with 2 loops found only %d frames' %@@@",
+      "@@@STEP_LOG_LINE@python.inline@                  total_completed_frames)@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Get frame avg/min/max for the middle 25 frames.@@@",
+      "@@@STEP_LOG_LINE@python.inline@start = (total_completed_frames - 25)/2@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'Got %d total completed frames. Using start_index of %d.' % (@@@",
+      "@@@STEP_LOG_LINE@python.inline@    total_completed_frames, start)@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@for frame_id, duration in completed_frame_id_and_duration[start:start+25]:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_max = max(frame_max, duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_min = min(frame_min, duration) if frame_min else duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_cumulative += duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results = {}@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_max_us'] = frame_max@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_min_us'] = frame_min@@@",
-      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/total_frames@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'For %d frames got: %s' % (total_frames, perf_results)@@@",
+      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/25@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'For 25 frames got: %s' % perf_results@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@# Write perf_results to the output json.@@@",
       "@@@STEP_LOG_LINE@python.inline@with open(output_json_file, 'w') as f:@@@",
@@ -256,7 +270,7 @@
     "cmd": [
       "python",
       "-u",
-      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n    'main_frame_aborted',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])\n\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      frame_max = max(frame_max, current_frame_duration)\n      frame_min = (min(frame_min, current_frame_duration)\n                   if frame_min else current_frame_duration)\n      frame_cumulative += current_frame_duration\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/total_frames\nprint 'For %d frames got: %s' % (total_frames, perf_results)\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
+      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(\n      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])\n\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\n# Will contain tuples of frame_ids and their duration.\ncompleted_frame_id_and_duration = []\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      completed_frame_id_and_duration.append(\n          (frame_id, current_frame_duration))\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\ntotal_completed_frames = len(completed_frame_id_and_duration)\nif total_completed_frames < 25:\n  raise Exception('Even with 2 loops found only %d frames' %\n                  total_completed_frames)\n\n# Get frame avg/min/max for the middle 25 frames.\nstart = (total_completed_frames - 25)/2\nprint 'Got %d total completed frames. Using start_index of %d.' % (\n    total_completed_frames, start)\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\nfor frame_id, duration in completed_frame_id_and_duration[start:start+25]:\n  frame_max = max(frame_max, duration)\n  frame_min = min(frame_min, duration) if frame_min else duration\n  frame_cumulative += duration\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/25\nprint 'For 25 frames got: %s' % perf_results\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
       "[CLEANUP]/g3_try_tmp_1/lottie2.json",
       "/path/to/tmp/json",
       "lottie-web"
@@ -286,20 +300,19 @@
       "@@@STEP_LOG_LINE@python.inline@erroneous_termination_statuses = [@@@",
       "@@@STEP_LOG_LINE@python.inline@    'replaced_by_new_reporter_at_same_stage',@@@",
       "@@@STEP_LOG_LINE@python.inline@    'did_not_produce_frame',@@@",
-      "@@@STEP_LOG_LINE@python.inline@    'main_frame_aborted',@@@",
       "@@@STEP_LOG_LINE@python.inline@]@@@",
       "@@@STEP_LOG_LINE@python.inline@accepted_termination_statuses = []@@@",
       "@@@STEP_LOG_LINE@python.inline@if renderer == 'skottie-wasm':@@@",
       "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@elif renderer == 'lottie-web':@@@",
-      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(@@@",
+      "@@@STEP_LOG_LINE@python.inline@      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@current_frame_duration = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@total_frames = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@frame_id_to_start_ts = {}@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Will contain tuples of frame_ids and their duration.@@@",
+      "@@@STEP_LOG_LINE@python.inline@completed_frame_id_and_duration = []@@@",
       "@@@STEP_LOG_LINE@python.inline@for trace in trace_json['traceEvents']:@@@",
       "@@@STEP_LOG_LINE@python.inline@  if 'PipelineReporter' in trace['name']:@@@",
       "@@@STEP_LOG_LINE@python.inline@    frame_id = trace['id']@@@",
@@ -313,10 +326,8 @@
       "@@@STEP_LOG_LINE@python.inline@        continue@@@",
       "@@@STEP_LOG_LINE@python.inline@      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]@@@",
       "@@@STEP_LOG_LINE@python.inline@      total_frames += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_max = max(frame_max, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_min = (min(frame_min, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@                   if frame_min else current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_cumulative += current_frame_duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@      completed_frame_id_and_duration.append(@@@",
+      "@@@STEP_LOG_LINE@python.inline@          (frame_id, current_frame_duration))@@@",
       "@@@STEP_LOG_LINE@python.inline@      # We are done with this frame_id so remove it from the dict.@@@",
       "@@@STEP_LOG_LINE@python.inline@      frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@      print '%d (%s with %s): %d' % (@@@",
@@ -330,11 +341,28 @@
       "@@@STEP_LOG_LINE@python.inline@            frame_id, args['termination_status'])@@@",
       "@@@STEP_LOG_LINE@python.inline@        frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@total_completed_frames = len(completed_frame_id_and_duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@if total_completed_frames < 25:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  raise Exception('Even with 2 loops found only %d frames' %@@@",
+      "@@@STEP_LOG_LINE@python.inline@                  total_completed_frames)@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Get frame avg/min/max for the middle 25 frames.@@@",
+      "@@@STEP_LOG_LINE@python.inline@start = (total_completed_frames - 25)/2@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'Got %d total completed frames. Using start_index of %d.' % (@@@",
+      "@@@STEP_LOG_LINE@python.inline@    total_completed_frames, start)@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@for frame_id, duration in completed_frame_id_and_duration[start:start+25]:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_max = max(frame_max, duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_min = min(frame_min, duration) if frame_min else duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_cumulative += duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results = {}@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_max_us'] = frame_max@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_min_us'] = frame_min@@@",
-      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/total_frames@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'For %d frames got: %s' % (total_frames, perf_results)@@@",
+      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/25@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'For 25 frames got: %s' % perf_results@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@# Write perf_results to the output json.@@@",
       "@@@STEP_LOG_LINE@python.inline@with open(output_json_file, 'w') as f:@@@",
@@ -363,7 +391,7 @@
     "cmd": [
       "python",
       "-u",
-      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n    'main_frame_aborted',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])\n\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      frame_max = max(frame_max, current_frame_duration)\n      frame_min = (min(frame_min, current_frame_duration)\n                   if frame_min else current_frame_duration)\n      frame_cumulative += current_frame_duration\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/total_frames\nprint 'For %d frames got: %s' % (total_frames, perf_results)\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
+      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(\n      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])\n\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\n# Will contain tuples of frame_ids and their duration.\ncompleted_frame_id_and_duration = []\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      completed_frame_id_and_duration.append(\n          (frame_id, current_frame_duration))\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\ntotal_completed_frames = len(completed_frame_id_and_duration)\nif total_completed_frames < 25:\n  raise Exception('Even with 2 loops found only %d frames' %\n                  total_completed_frames)\n\n# Get frame avg/min/max for the middle 25 frames.\nstart = (total_completed_frames - 25)/2\nprint 'Got %d total completed frames. Using start_index of %d.' % (\n    total_completed_frames, start)\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\nfor frame_id, duration in completed_frame_id_and_duration[start:start+25]:\n  frame_max = max(frame_max, duration)\n  frame_min = min(frame_min, duration) if frame_min else duration\n  frame_cumulative += duration\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/25\nprint 'For 25 frames got: %s' % perf_results\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
       "[CLEANUP]/g3_try_tmp_1/lottie3.json",
       "/path/to/tmp/json",
       "lottie-web"
@@ -393,20 +421,19 @@
       "@@@STEP_LOG_LINE@python.inline@erroneous_termination_statuses = [@@@",
       "@@@STEP_LOG_LINE@python.inline@    'replaced_by_new_reporter_at_same_stage',@@@",
       "@@@STEP_LOG_LINE@python.inline@    'did_not_produce_frame',@@@",
-      "@@@STEP_LOG_LINE@python.inline@    'main_frame_aborted',@@@",
       "@@@STEP_LOG_LINE@python.inline@]@@@",
       "@@@STEP_LOG_LINE@python.inline@accepted_termination_statuses = []@@@",
       "@@@STEP_LOG_LINE@python.inline@if renderer == 'skottie-wasm':@@@",
       "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@elif renderer == 'lottie-web':@@@",
-      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(@@@",
+      "@@@STEP_LOG_LINE@python.inline@      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@current_frame_duration = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@total_frames = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@frame_id_to_start_ts = {}@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Will contain tuples of frame_ids and their duration.@@@",
+      "@@@STEP_LOG_LINE@python.inline@completed_frame_id_and_duration = []@@@",
       "@@@STEP_LOG_LINE@python.inline@for trace in trace_json['traceEvents']:@@@",
       "@@@STEP_LOG_LINE@python.inline@  if 'PipelineReporter' in trace['name']:@@@",
       "@@@STEP_LOG_LINE@python.inline@    frame_id = trace['id']@@@",
@@ -420,10 +447,8 @@
       "@@@STEP_LOG_LINE@python.inline@        continue@@@",
       "@@@STEP_LOG_LINE@python.inline@      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]@@@",
       "@@@STEP_LOG_LINE@python.inline@      total_frames += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_max = max(frame_max, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_min = (min(frame_min, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@                   if frame_min else current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_cumulative += current_frame_duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@      completed_frame_id_and_duration.append(@@@",
+      "@@@STEP_LOG_LINE@python.inline@          (frame_id, current_frame_duration))@@@",
       "@@@STEP_LOG_LINE@python.inline@      # We are done with this frame_id so remove it from the dict.@@@",
       "@@@STEP_LOG_LINE@python.inline@      frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@      print '%d (%s with %s): %d' % (@@@",
@@ -437,11 +462,28 @@
       "@@@STEP_LOG_LINE@python.inline@            frame_id, args['termination_status'])@@@",
       "@@@STEP_LOG_LINE@python.inline@        frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@total_completed_frames = len(completed_frame_id_and_duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@if total_completed_frames < 25:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  raise Exception('Even with 2 loops found only %d frames' %@@@",
+      "@@@STEP_LOG_LINE@python.inline@                  total_completed_frames)@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Get frame avg/min/max for the middle 25 frames.@@@",
+      "@@@STEP_LOG_LINE@python.inline@start = (total_completed_frames - 25)/2@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'Got %d total completed frames. Using start_index of %d.' % (@@@",
+      "@@@STEP_LOG_LINE@python.inline@    total_completed_frames, start)@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@for frame_id, duration in completed_frame_id_and_duration[start:start+25]:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_max = max(frame_max, duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_min = min(frame_min, duration) if frame_min else duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_cumulative += duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results = {}@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_max_us'] = frame_max@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_min_us'] = frame_min@@@",
-      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/total_frames@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'For %d frames got: %s' % (total_frames, perf_results)@@@",
+      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/25@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'For 25 frames got: %s' % perf_results@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@# Write perf_results to the output json.@@@",
       "@@@STEP_LOG_LINE@python.inline@with open(output_json_file, 'w') as f:@@@",
diff --git a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf.json b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf.json
index 1b604c8..fc439df 100644
--- a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf.json
+++ b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf.json
@@ -151,7 +151,7 @@
     "cmd": [
       "python",
       "-u",
-      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n    'main_frame_aborted',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])\n\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      frame_max = max(frame_max, current_frame_duration)\n      frame_min = (min(frame_min, current_frame_duration)\n                   if frame_min else current_frame_duration)\n      frame_cumulative += current_frame_duration\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/total_frames\nprint 'For %d frames got: %s' % (total_frames, perf_results)\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
+      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(\n      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])\n\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\n# Will contain tuples of frame_ids and their duration.\ncompleted_frame_id_and_duration = []\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      completed_frame_id_and_duration.append(\n          (frame_id, current_frame_duration))\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\ntotal_completed_frames = len(completed_frame_id_and_duration)\nif total_completed_frames < 25:\n  raise Exception('Even with 2 loops found only %d frames' %\n                  total_completed_frames)\n\n# Get frame avg/min/max for the middle 25 frames.\nstart = (total_completed_frames - 25)/2\nprint 'Got %d total completed frames. Using start_index of %d.' % (\n    total_completed_frames, start)\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\nfor frame_id, duration in completed_frame_id_and_duration[start:start+25]:\n  frame_max = max(frame_max, duration)\n  frame_min = min(frame_min, duration) if frame_min else duration\n  frame_cumulative += duration\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/25\nprint 'For 25 frames got: %s' % perf_results\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
       "[CLEANUP]/g3_try_tmp_1/lottie1.json",
       "/path/to/tmp/json",
       "skottie-wasm"
@@ -181,20 +181,19 @@
       "@@@STEP_LOG_LINE@python.inline@erroneous_termination_statuses = [@@@",
       "@@@STEP_LOG_LINE@python.inline@    'replaced_by_new_reporter_at_same_stage',@@@",
       "@@@STEP_LOG_LINE@python.inline@    'did_not_produce_frame',@@@",
-      "@@@STEP_LOG_LINE@python.inline@    'main_frame_aborted',@@@",
       "@@@STEP_LOG_LINE@python.inline@]@@@",
       "@@@STEP_LOG_LINE@python.inline@accepted_termination_statuses = []@@@",
       "@@@STEP_LOG_LINE@python.inline@if renderer == 'skottie-wasm':@@@",
       "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@elif renderer == 'lottie-web':@@@",
-      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(@@@",
+      "@@@STEP_LOG_LINE@python.inline@      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@current_frame_duration = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@total_frames = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@frame_id_to_start_ts = {}@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Will contain tuples of frame_ids and their duration.@@@",
+      "@@@STEP_LOG_LINE@python.inline@completed_frame_id_and_duration = []@@@",
       "@@@STEP_LOG_LINE@python.inline@for trace in trace_json['traceEvents']:@@@",
       "@@@STEP_LOG_LINE@python.inline@  if 'PipelineReporter' in trace['name']:@@@",
       "@@@STEP_LOG_LINE@python.inline@    frame_id = trace['id']@@@",
@@ -208,10 +207,8 @@
       "@@@STEP_LOG_LINE@python.inline@        continue@@@",
       "@@@STEP_LOG_LINE@python.inline@      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]@@@",
       "@@@STEP_LOG_LINE@python.inline@      total_frames += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_max = max(frame_max, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_min = (min(frame_min, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@                   if frame_min else current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_cumulative += current_frame_duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@      completed_frame_id_and_duration.append(@@@",
+      "@@@STEP_LOG_LINE@python.inline@          (frame_id, current_frame_duration))@@@",
       "@@@STEP_LOG_LINE@python.inline@      # We are done with this frame_id so remove it from the dict.@@@",
       "@@@STEP_LOG_LINE@python.inline@      frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@      print '%d (%s with %s): %d' % (@@@",
@@ -225,11 +222,28 @@
       "@@@STEP_LOG_LINE@python.inline@            frame_id, args['termination_status'])@@@",
       "@@@STEP_LOG_LINE@python.inline@        frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@total_completed_frames = len(completed_frame_id_and_duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@if total_completed_frames < 25:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  raise Exception('Even with 2 loops found only %d frames' %@@@",
+      "@@@STEP_LOG_LINE@python.inline@                  total_completed_frames)@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Get frame avg/min/max for the middle 25 frames.@@@",
+      "@@@STEP_LOG_LINE@python.inline@start = (total_completed_frames - 25)/2@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'Got %d total completed frames. Using start_index of %d.' % (@@@",
+      "@@@STEP_LOG_LINE@python.inline@    total_completed_frames, start)@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@for frame_id, duration in completed_frame_id_and_duration[start:start+25]:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_max = max(frame_max, duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_min = min(frame_min, duration) if frame_min else duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_cumulative += duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results = {}@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_max_us'] = frame_max@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_min_us'] = frame_min@@@",
-      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/total_frames@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'For %d frames got: %s' % (total_frames, perf_results)@@@",
+      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/25@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'For 25 frames got: %s' % perf_results@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@# Write perf_results to the output json.@@@",
       "@@@STEP_LOG_LINE@python.inline@with open(output_json_file, 'w') as f:@@@",
@@ -262,7 +276,7 @@
     "cmd": [
       "python",
       "-u",
-      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n    'main_frame_aborted',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])\n\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      frame_max = max(frame_max, current_frame_duration)\n      frame_min = (min(frame_min, current_frame_duration)\n                   if frame_min else current_frame_duration)\n      frame_cumulative += current_frame_duration\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/total_frames\nprint 'For %d frames got: %s' % (total_frames, perf_results)\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
+      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(\n      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])\n\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\n# Will contain tuples of frame_ids and their duration.\ncompleted_frame_id_and_duration = []\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      completed_frame_id_and_duration.append(\n          (frame_id, current_frame_duration))\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\ntotal_completed_frames = len(completed_frame_id_and_duration)\nif total_completed_frames < 25:\n  raise Exception('Even with 2 loops found only %d frames' %\n                  total_completed_frames)\n\n# Get frame avg/min/max for the middle 25 frames.\nstart = (total_completed_frames - 25)/2\nprint 'Got %d total completed frames. Using start_index of %d.' % (\n    total_completed_frames, start)\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\nfor frame_id, duration in completed_frame_id_and_duration[start:start+25]:\n  frame_max = max(frame_max, duration)\n  frame_min = min(frame_min, duration) if frame_min else duration\n  frame_cumulative += duration\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/25\nprint 'For 25 frames got: %s' % perf_results\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
       "[CLEANUP]/g3_try_tmp_1/lottie2.json",
       "/path/to/tmp/json",
       "skottie-wasm"
@@ -292,20 +306,19 @@
       "@@@STEP_LOG_LINE@python.inline@erroneous_termination_statuses = [@@@",
       "@@@STEP_LOG_LINE@python.inline@    'replaced_by_new_reporter_at_same_stage',@@@",
       "@@@STEP_LOG_LINE@python.inline@    'did_not_produce_frame',@@@",
-      "@@@STEP_LOG_LINE@python.inline@    'main_frame_aborted',@@@",
       "@@@STEP_LOG_LINE@python.inline@]@@@",
       "@@@STEP_LOG_LINE@python.inline@accepted_termination_statuses = []@@@",
       "@@@STEP_LOG_LINE@python.inline@if renderer == 'skottie-wasm':@@@",
       "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@elif renderer == 'lottie-web':@@@",
-      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(@@@",
+      "@@@STEP_LOG_LINE@python.inline@      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@current_frame_duration = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@total_frames = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@frame_id_to_start_ts = {}@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Will contain tuples of frame_ids and their duration.@@@",
+      "@@@STEP_LOG_LINE@python.inline@completed_frame_id_and_duration = []@@@",
       "@@@STEP_LOG_LINE@python.inline@for trace in trace_json['traceEvents']:@@@",
       "@@@STEP_LOG_LINE@python.inline@  if 'PipelineReporter' in trace['name']:@@@",
       "@@@STEP_LOG_LINE@python.inline@    frame_id = trace['id']@@@",
@@ -319,10 +332,8 @@
       "@@@STEP_LOG_LINE@python.inline@        continue@@@",
       "@@@STEP_LOG_LINE@python.inline@      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]@@@",
       "@@@STEP_LOG_LINE@python.inline@      total_frames += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_max = max(frame_max, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_min = (min(frame_min, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@                   if frame_min else current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_cumulative += current_frame_duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@      completed_frame_id_and_duration.append(@@@",
+      "@@@STEP_LOG_LINE@python.inline@          (frame_id, current_frame_duration))@@@",
       "@@@STEP_LOG_LINE@python.inline@      # We are done with this frame_id so remove it from the dict.@@@",
       "@@@STEP_LOG_LINE@python.inline@      frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@      print '%d (%s with %s): %d' % (@@@",
@@ -336,11 +347,28 @@
       "@@@STEP_LOG_LINE@python.inline@            frame_id, args['termination_status'])@@@",
       "@@@STEP_LOG_LINE@python.inline@        frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@total_completed_frames = len(completed_frame_id_and_duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@if total_completed_frames < 25:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  raise Exception('Even with 2 loops found only %d frames' %@@@",
+      "@@@STEP_LOG_LINE@python.inline@                  total_completed_frames)@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Get frame avg/min/max for the middle 25 frames.@@@",
+      "@@@STEP_LOG_LINE@python.inline@start = (total_completed_frames - 25)/2@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'Got %d total completed frames. Using start_index of %d.' % (@@@",
+      "@@@STEP_LOG_LINE@python.inline@    total_completed_frames, start)@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@for frame_id, duration in completed_frame_id_and_duration[start:start+25]:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_max = max(frame_max, duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_min = min(frame_min, duration) if frame_min else duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_cumulative += duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results = {}@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_max_us'] = frame_max@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_min_us'] = frame_min@@@",
-      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/total_frames@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'For %d frames got: %s' % (total_frames, perf_results)@@@",
+      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/25@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'For 25 frames got: %s' % perf_results@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@# Write perf_results to the output json.@@@",
       "@@@STEP_LOG_LINE@python.inline@with open(output_json_file, 'w') as f:@@@",
@@ -373,7 +401,7 @@
     "cmd": [
       "python",
       "-u",
-      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n    'main_frame_aborted',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])\n\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      frame_max = max(frame_max, current_frame_duration)\n      frame_min = (min(frame_min, current_frame_duration)\n                   if frame_min else current_frame_duration)\n      frame_cumulative += current_frame_duration\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/total_frames\nprint 'For %d frames got: %s' % (total_frames, perf_results)\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
+      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(\n      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])\n\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\n# Will contain tuples of frame_ids and their duration.\ncompleted_frame_id_and_duration = []\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      completed_frame_id_and_duration.append(\n          (frame_id, current_frame_duration))\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\ntotal_completed_frames = len(completed_frame_id_and_duration)\nif total_completed_frames < 25:\n  raise Exception('Even with 2 loops found only %d frames' %\n                  total_completed_frames)\n\n# Get frame avg/min/max for the middle 25 frames.\nstart = (total_completed_frames - 25)/2\nprint 'Got %d total completed frames. Using start_index of %d.' % (\n    total_completed_frames, start)\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\nfor frame_id, duration in completed_frame_id_and_duration[start:start+25]:\n  frame_max = max(frame_max, duration)\n  frame_min = min(frame_min, duration) if frame_min else duration\n  frame_cumulative += duration\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/25\nprint 'For 25 frames got: %s' % perf_results\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
       "[CLEANUP]/g3_try_tmp_1/lottie3.json",
       "/path/to/tmp/json",
       "skottie-wasm"
@@ -403,20 +431,19 @@
       "@@@STEP_LOG_LINE@python.inline@erroneous_termination_statuses = [@@@",
       "@@@STEP_LOG_LINE@python.inline@    'replaced_by_new_reporter_at_same_stage',@@@",
       "@@@STEP_LOG_LINE@python.inline@    'did_not_produce_frame',@@@",
-      "@@@STEP_LOG_LINE@python.inline@    'main_frame_aborted',@@@",
       "@@@STEP_LOG_LINE@python.inline@]@@@",
       "@@@STEP_LOG_LINE@python.inline@accepted_termination_statuses = []@@@",
       "@@@STEP_LOG_LINE@python.inline@if renderer == 'skottie-wasm':@@@",
       "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@elif renderer == 'lottie-web':@@@",
-      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(@@@",
+      "@@@STEP_LOG_LINE@python.inline@      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@current_frame_duration = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@total_frames = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@frame_id_to_start_ts = {}@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Will contain tuples of frame_ids and their duration.@@@",
+      "@@@STEP_LOG_LINE@python.inline@completed_frame_id_and_duration = []@@@",
       "@@@STEP_LOG_LINE@python.inline@for trace in trace_json['traceEvents']:@@@",
       "@@@STEP_LOG_LINE@python.inline@  if 'PipelineReporter' in trace['name']:@@@",
       "@@@STEP_LOG_LINE@python.inline@    frame_id = trace['id']@@@",
@@ -430,10 +457,8 @@
       "@@@STEP_LOG_LINE@python.inline@        continue@@@",
       "@@@STEP_LOG_LINE@python.inline@      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]@@@",
       "@@@STEP_LOG_LINE@python.inline@      total_frames += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_max = max(frame_max, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_min = (min(frame_min, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@                   if frame_min else current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_cumulative += current_frame_duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@      completed_frame_id_and_duration.append(@@@",
+      "@@@STEP_LOG_LINE@python.inline@          (frame_id, current_frame_duration))@@@",
       "@@@STEP_LOG_LINE@python.inline@      # We are done with this frame_id so remove it from the dict.@@@",
       "@@@STEP_LOG_LINE@python.inline@      frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@      print '%d (%s with %s): %d' % (@@@",
@@ -447,11 +472,28 @@
       "@@@STEP_LOG_LINE@python.inline@            frame_id, args['termination_status'])@@@",
       "@@@STEP_LOG_LINE@python.inline@        frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@total_completed_frames = len(completed_frame_id_and_duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@if total_completed_frames < 25:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  raise Exception('Even with 2 loops found only %d frames' %@@@",
+      "@@@STEP_LOG_LINE@python.inline@                  total_completed_frames)@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Get frame avg/min/max for the middle 25 frames.@@@",
+      "@@@STEP_LOG_LINE@python.inline@start = (total_completed_frames - 25)/2@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'Got %d total completed frames. Using start_index of %d.' % (@@@",
+      "@@@STEP_LOG_LINE@python.inline@    total_completed_frames, start)@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@for frame_id, duration in completed_frame_id_and_duration[start:start+25]:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_max = max(frame_max, duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_min = min(frame_min, duration) if frame_min else duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_cumulative += duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results = {}@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_max_us'] = frame_max@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_min_us'] = frame_min@@@",
-      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/total_frames@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'For %d frames got: %s' % (total_frames, perf_results)@@@",
+      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/25@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'For 25 frames got: %s' % perf_results@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@# Write perf_results to the output json.@@@",
       "@@@STEP_LOG_LINE@python.inline@with open(output_json_file, 'w') as f:@@@",
diff --git a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf_trybot.json b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf_trybot.json
index f43d3e4..772ec3a 100644
--- a/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf_trybot.json
+++ b/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/skottie_wasm_perf_trybot.json
@@ -153,7 +153,7 @@
     "cmd": [
       "python",
       "-u",
-      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n    'main_frame_aborted',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])\n\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      frame_max = max(frame_max, current_frame_duration)\n      frame_min = (min(frame_min, current_frame_duration)\n                   if frame_min else current_frame_duration)\n      frame_cumulative += current_frame_duration\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/total_frames\nprint 'For %d frames got: %s' % (total_frames, perf_results)\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
+      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(\n      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])\n\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\n# Will contain tuples of frame_ids and their duration.\ncompleted_frame_id_and_duration = []\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      completed_frame_id_and_duration.append(\n          (frame_id, current_frame_duration))\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\ntotal_completed_frames = len(completed_frame_id_and_duration)\nif total_completed_frames < 25:\n  raise Exception('Even with 2 loops found only %d frames' %\n                  total_completed_frames)\n\n# Get frame avg/min/max for the middle 25 frames.\nstart = (total_completed_frames - 25)/2\nprint 'Got %d total completed frames. Using start_index of %d.' % (\n    total_completed_frames, start)\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\nfor frame_id, duration in completed_frame_id_and_duration[start:start+25]:\n  frame_max = max(frame_max, duration)\n  frame_min = min(frame_min, duration) if frame_min else duration\n  frame_cumulative += duration\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/25\nprint 'For 25 frames got: %s' % perf_results\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
       "[CLEANUP]/g3_try_tmp_1/lottie1.json",
       "/path/to/tmp/json",
       "skottie-wasm"
@@ -183,20 +183,19 @@
       "@@@STEP_LOG_LINE@python.inline@erroneous_termination_statuses = [@@@",
       "@@@STEP_LOG_LINE@python.inline@    'replaced_by_new_reporter_at_same_stage',@@@",
       "@@@STEP_LOG_LINE@python.inline@    'did_not_produce_frame',@@@",
-      "@@@STEP_LOG_LINE@python.inline@    'main_frame_aborted',@@@",
       "@@@STEP_LOG_LINE@python.inline@]@@@",
       "@@@STEP_LOG_LINE@python.inline@accepted_termination_statuses = []@@@",
       "@@@STEP_LOG_LINE@python.inline@if renderer == 'skottie-wasm':@@@",
       "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@elif renderer == 'lottie-web':@@@",
-      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(@@@",
+      "@@@STEP_LOG_LINE@python.inline@      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@current_frame_duration = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@total_frames = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@frame_id_to_start_ts = {}@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Will contain tuples of frame_ids and their duration.@@@",
+      "@@@STEP_LOG_LINE@python.inline@completed_frame_id_and_duration = []@@@",
       "@@@STEP_LOG_LINE@python.inline@for trace in trace_json['traceEvents']:@@@",
       "@@@STEP_LOG_LINE@python.inline@  if 'PipelineReporter' in trace['name']:@@@",
       "@@@STEP_LOG_LINE@python.inline@    frame_id = trace['id']@@@",
@@ -210,10 +209,8 @@
       "@@@STEP_LOG_LINE@python.inline@        continue@@@",
       "@@@STEP_LOG_LINE@python.inline@      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]@@@",
       "@@@STEP_LOG_LINE@python.inline@      total_frames += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_max = max(frame_max, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_min = (min(frame_min, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@                   if frame_min else current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_cumulative += current_frame_duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@      completed_frame_id_and_duration.append(@@@",
+      "@@@STEP_LOG_LINE@python.inline@          (frame_id, current_frame_duration))@@@",
       "@@@STEP_LOG_LINE@python.inline@      # We are done with this frame_id so remove it from the dict.@@@",
       "@@@STEP_LOG_LINE@python.inline@      frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@      print '%d (%s with %s): %d' % (@@@",
@@ -227,11 +224,28 @@
       "@@@STEP_LOG_LINE@python.inline@            frame_id, args['termination_status'])@@@",
       "@@@STEP_LOG_LINE@python.inline@        frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@total_completed_frames = len(completed_frame_id_and_duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@if total_completed_frames < 25:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  raise Exception('Even with 2 loops found only %d frames' %@@@",
+      "@@@STEP_LOG_LINE@python.inline@                  total_completed_frames)@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Get frame avg/min/max for the middle 25 frames.@@@",
+      "@@@STEP_LOG_LINE@python.inline@start = (total_completed_frames - 25)/2@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'Got %d total completed frames. Using start_index of %d.' % (@@@",
+      "@@@STEP_LOG_LINE@python.inline@    total_completed_frames, start)@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@for frame_id, duration in completed_frame_id_and_duration[start:start+25]:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_max = max(frame_max, duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_min = min(frame_min, duration) if frame_min else duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_cumulative += duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results = {}@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_max_us'] = frame_max@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_min_us'] = frame_min@@@",
-      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/total_frames@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'For %d frames got: %s' % (total_frames, perf_results)@@@",
+      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/25@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'For 25 frames got: %s' % perf_results@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@# Write perf_results to the output json.@@@",
       "@@@STEP_LOG_LINE@python.inline@with open(output_json_file, 'w') as f:@@@",
@@ -264,7 +278,7 @@
     "cmd": [
       "python",
       "-u",
-      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n    'main_frame_aborted',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])\n\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      frame_max = max(frame_max, current_frame_duration)\n      frame_min = (min(frame_min, current_frame_duration)\n                   if frame_min else current_frame_duration)\n      frame_cumulative += current_frame_duration\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/total_frames\nprint 'For %d frames got: %s' % (total_frames, perf_results)\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
+      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(\n      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])\n\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\n# Will contain tuples of frame_ids and their duration.\ncompleted_frame_id_and_duration = []\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      completed_frame_id_and_duration.append(\n          (frame_id, current_frame_duration))\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\ntotal_completed_frames = len(completed_frame_id_and_duration)\nif total_completed_frames < 25:\n  raise Exception('Even with 2 loops found only %d frames' %\n                  total_completed_frames)\n\n# Get frame avg/min/max for the middle 25 frames.\nstart = (total_completed_frames - 25)/2\nprint 'Got %d total completed frames. Using start_index of %d.' % (\n    total_completed_frames, start)\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\nfor frame_id, duration in completed_frame_id_and_duration[start:start+25]:\n  frame_max = max(frame_max, duration)\n  frame_min = min(frame_min, duration) if frame_min else duration\n  frame_cumulative += duration\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/25\nprint 'For 25 frames got: %s' % perf_results\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
       "[CLEANUP]/g3_try_tmp_1/lottie2.json",
       "/path/to/tmp/json",
       "skottie-wasm"
@@ -294,20 +308,19 @@
       "@@@STEP_LOG_LINE@python.inline@erroneous_termination_statuses = [@@@",
       "@@@STEP_LOG_LINE@python.inline@    'replaced_by_new_reporter_at_same_stage',@@@",
       "@@@STEP_LOG_LINE@python.inline@    'did_not_produce_frame',@@@",
-      "@@@STEP_LOG_LINE@python.inline@    'main_frame_aborted',@@@",
       "@@@STEP_LOG_LINE@python.inline@]@@@",
       "@@@STEP_LOG_LINE@python.inline@accepted_termination_statuses = []@@@",
       "@@@STEP_LOG_LINE@python.inline@if renderer == 'skottie-wasm':@@@",
       "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@elif renderer == 'lottie-web':@@@",
-      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(@@@",
+      "@@@STEP_LOG_LINE@python.inline@      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@current_frame_duration = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@total_frames = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@frame_id_to_start_ts = {}@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Will contain tuples of frame_ids and their duration.@@@",
+      "@@@STEP_LOG_LINE@python.inline@completed_frame_id_and_duration = []@@@",
       "@@@STEP_LOG_LINE@python.inline@for trace in trace_json['traceEvents']:@@@",
       "@@@STEP_LOG_LINE@python.inline@  if 'PipelineReporter' in trace['name']:@@@",
       "@@@STEP_LOG_LINE@python.inline@    frame_id = trace['id']@@@",
@@ -321,10 +334,8 @@
       "@@@STEP_LOG_LINE@python.inline@        continue@@@",
       "@@@STEP_LOG_LINE@python.inline@      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]@@@",
       "@@@STEP_LOG_LINE@python.inline@      total_frames += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_max = max(frame_max, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_min = (min(frame_min, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@                   if frame_min else current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_cumulative += current_frame_duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@      completed_frame_id_and_duration.append(@@@",
+      "@@@STEP_LOG_LINE@python.inline@          (frame_id, current_frame_duration))@@@",
       "@@@STEP_LOG_LINE@python.inline@      # We are done with this frame_id so remove it from the dict.@@@",
       "@@@STEP_LOG_LINE@python.inline@      frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@      print '%d (%s with %s): %d' % (@@@",
@@ -338,11 +349,28 @@
       "@@@STEP_LOG_LINE@python.inline@            frame_id, args['termination_status'])@@@",
       "@@@STEP_LOG_LINE@python.inline@        frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@total_completed_frames = len(completed_frame_id_and_duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@if total_completed_frames < 25:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  raise Exception('Even with 2 loops found only %d frames' %@@@",
+      "@@@STEP_LOG_LINE@python.inline@                  total_completed_frames)@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Get frame avg/min/max for the middle 25 frames.@@@",
+      "@@@STEP_LOG_LINE@python.inline@start = (total_completed_frames - 25)/2@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'Got %d total completed frames. Using start_index of %d.' % (@@@",
+      "@@@STEP_LOG_LINE@python.inline@    total_completed_frames, start)@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@for frame_id, duration in completed_frame_id_and_duration[start:start+25]:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_max = max(frame_max, duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_min = min(frame_min, duration) if frame_min else duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_cumulative += duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results = {}@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_max_us'] = frame_max@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_min_us'] = frame_min@@@",
-      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/total_frames@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'For %d frames got: %s' % (total_frames, perf_results)@@@",
+      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/25@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'For 25 frames got: %s' % perf_results@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@# Write perf_results to the output json.@@@",
       "@@@STEP_LOG_LINE@python.inline@with open(output_json_file, 'w') as f:@@@",
@@ -375,7 +403,7 @@
     "cmd": [
       "python",
       "-u",
-      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n    'main_frame_aborted',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])\n\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      frame_max = max(frame_max, current_frame_duration)\n      frame_min = (min(frame_min, current_frame_duration)\n                   if frame_min else current_frame_duration)\n      frame_cumulative += current_frame_duration\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/total_frames\nprint 'For %d frames got: %s' % (total_frames, perf_results)\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
+      "\nimport json\nimport sys\n\ntrace_output = sys.argv[1]\nwith open(trace_output, 'r') as f:\n  trace_json = json.load(f)\noutput_json_file = sys.argv[2]\nrenderer = sys.argv[3]\n\nerroneous_termination_statuses = [\n    'replaced_by_new_reporter_at_same_stage',\n    'did_not_produce_frame',\n]\naccepted_termination_statuses = []\nif renderer == 'skottie-wasm':\n  accepted_termination_statuses.extend(['main_frame_aborted'])\nelif renderer == 'lottie-web':\n  accepted_termination_statuses.extend(\n      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])\n\ncurrent_frame_duration = 0\ntotal_frames = 0\nframe_id_to_start_ts = {}\n# Will contain tuples of frame_ids and their duration.\ncompleted_frame_id_and_duration = []\nfor trace in trace_json['traceEvents']:\n  if 'PipelineReporter' in trace['name']:\n    frame_id = trace['id']\n    args = trace.get('args')\n    if args and args.get('step') == 'BeginImplFrameToSendBeginMainFrame':\n      frame_id_to_start_ts[frame_id] = trace['ts']\n    elif args and (args.get('termination_status') in\n                   accepted_termination_statuses):\n      if not frame_id_to_start_ts.get(frame_id):\n        print '[No start ts found for %s]' % frame_id\n        continue\n      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]\n      total_frames += 1\n      completed_frame_id_and_duration.append(\n          (frame_id, current_frame_duration))\n      # We are done with this frame_id so remove it from the dict.\n      frame_id_to_start_ts.pop(frame_id)\n      print '%d (%s with %s): %d' % (\n          total_frames, frame_id, args['termination_status'],\n          current_frame_duration)\n    elif args and (args.get('termination_status') in\n                   erroneous_termination_statuses):\n      # Invalidate previously collected results for this frame_id.\n      if frame_id_to_start_ts.get(frame_id):\n        print '[Invalidating %s due to %s]' % (\n            frame_id, args['termination_status'])\n        frame_id_to_start_ts.pop(frame_id)\n\ntotal_completed_frames = len(completed_frame_id_and_duration)\nif total_completed_frames < 25:\n  raise Exception('Even with 2 loops found only %d frames' %\n                  total_completed_frames)\n\n# Get frame avg/min/max for the middle 25 frames.\nstart = (total_completed_frames - 25)/2\nprint 'Got %d total completed frames. Using start_index of %d.' % (\n    total_completed_frames, start)\nframe_max = 0\nframe_min = 0\nframe_cumulative = 0\nfor frame_id, duration in completed_frame_id_and_duration[start:start+25]:\n  frame_max = max(frame_max, duration)\n  frame_min = min(frame_min, duration) if frame_min else duration\n  frame_cumulative += duration\n\nperf_results = {}\nperf_results['frame_max_us'] = frame_max\nperf_results['frame_min_us'] = frame_min\nperf_results['frame_avg_us'] = frame_cumulative/25\nprint 'For 25 frames got: %s' % perf_results\n\n# Write perf_results to the output json.\nwith open(output_json_file, 'w') as f:\n  f.write(json.dumps(perf_results))\n",
       "[CLEANUP]/g3_try_tmp_1/lottie3.json",
       "/path/to/tmp/json",
       "skottie-wasm"
@@ -405,20 +433,19 @@
       "@@@STEP_LOG_LINE@python.inline@erroneous_termination_statuses = [@@@",
       "@@@STEP_LOG_LINE@python.inline@    'replaced_by_new_reporter_at_same_stage',@@@",
       "@@@STEP_LOG_LINE@python.inline@    'did_not_produce_frame',@@@",
-      "@@@STEP_LOG_LINE@python.inline@    'main_frame_aborted',@@@",
       "@@@STEP_LOG_LINE@python.inline@]@@@",
       "@@@STEP_LOG_LINE@python.inline@accepted_termination_statuses = []@@@",
       "@@@STEP_LOG_LINE@python.inline@if renderer == 'skottie-wasm':@@@",
       "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@elif renderer == 'lottie-web':@@@",
-      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])@@@",
+      "@@@STEP_LOG_LINE@python.inline@  accepted_termination_statuses.extend(@@@",
+      "@@@STEP_LOG_LINE@python.inline@      ['missed_frame', 'submitted_frame', 'main_frame_aborted'])@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
-      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@current_frame_duration = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@total_frames = 0@@@",
       "@@@STEP_LOG_LINE@python.inline@frame_id_to_start_ts = {}@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Will contain tuples of frame_ids and their duration.@@@",
+      "@@@STEP_LOG_LINE@python.inline@completed_frame_id_and_duration = []@@@",
       "@@@STEP_LOG_LINE@python.inline@for trace in trace_json['traceEvents']:@@@",
       "@@@STEP_LOG_LINE@python.inline@  if 'PipelineReporter' in trace['name']:@@@",
       "@@@STEP_LOG_LINE@python.inline@    frame_id = trace['id']@@@",
@@ -432,10 +459,8 @@
       "@@@STEP_LOG_LINE@python.inline@        continue@@@",
       "@@@STEP_LOG_LINE@python.inline@      current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]@@@",
       "@@@STEP_LOG_LINE@python.inline@      total_frames += 1@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_max = max(frame_max, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_min = (min(frame_min, current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@                   if frame_min else current_frame_duration)@@@",
-      "@@@STEP_LOG_LINE@python.inline@      frame_cumulative += current_frame_duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@      completed_frame_id_and_duration.append(@@@",
+      "@@@STEP_LOG_LINE@python.inline@          (frame_id, current_frame_duration))@@@",
       "@@@STEP_LOG_LINE@python.inline@      # We are done with this frame_id so remove it from the dict.@@@",
       "@@@STEP_LOG_LINE@python.inline@      frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@      print '%d (%s with %s): %d' % (@@@",
@@ -449,11 +474,28 @@
       "@@@STEP_LOG_LINE@python.inline@            frame_id, args['termination_status'])@@@",
       "@@@STEP_LOG_LINE@python.inline@        frame_id_to_start_ts.pop(frame_id)@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@total_completed_frames = len(completed_frame_id_and_duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@if total_completed_frames < 25:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  raise Exception('Even with 2 loops found only %d frames' %@@@",
+      "@@@STEP_LOG_LINE@python.inline@                  total_completed_frames)@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
+      "@@@STEP_LOG_LINE@python.inline@# Get frame avg/min/max for the middle 25 frames.@@@",
+      "@@@STEP_LOG_LINE@python.inline@start = (total_completed_frames - 25)/2@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'Got %d total completed frames. Using start_index of %d.' % (@@@",
+      "@@@STEP_LOG_LINE@python.inline@    total_completed_frames, start)@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_max = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_min = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@frame_cumulative = 0@@@",
+      "@@@STEP_LOG_LINE@python.inline@for frame_id, duration in completed_frame_id_and_duration[start:start+25]:@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_max = max(frame_max, duration)@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_min = min(frame_min, duration) if frame_min else duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@  frame_cumulative += duration@@@",
+      "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results = {}@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_max_us'] = frame_max@@@",
       "@@@STEP_LOG_LINE@python.inline@perf_results['frame_min_us'] = frame_min@@@",
-      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/total_frames@@@",
-      "@@@STEP_LOG_LINE@python.inline@print 'For %d frames got: %s' % (total_frames, perf_results)@@@",
+      "@@@STEP_LOG_LINE@python.inline@perf_results['frame_avg_us'] = frame_cumulative/25@@@",
+      "@@@STEP_LOG_LINE@python.inline@print 'For 25 frames got: %s' % perf_results@@@",
       "@@@STEP_LOG_LINE@python.inline@@@@",
       "@@@STEP_LOG_LINE@python.inline@# Write perf_results to the output json.@@@",
       "@@@STEP_LOG_LINE@python.inline@with open(output_json_file, 'w') as f:@@@",
diff --git a/infra/bots/recipes/perf_skottiewasm_lottieweb.py b/infra/bots/recipes/perf_skottiewasm_lottieweb.py
index 09cfc8c..ef9b4a3 100644
--- a/infra/bots/recipes/perf_skottiewasm_lottieweb.py
+++ b/infra/bots/recipes/perf_skottiewasm_lottieweb.py
@@ -178,20 +178,19 @@
   erroneous_termination_statuses = [
       'replaced_by_new_reporter_at_same_stage',
       'did_not_produce_frame',
-      'main_frame_aborted',
   ]
   accepted_termination_statuses = []
   if renderer == 'skottie-wasm':
     accepted_termination_statuses.extend(['main_frame_aborted'])
   elif renderer == 'lottie-web':
-    accepted_termination_statuses.extend(['missed_frame', 'submitted_frame'])
+    accepted_termination_statuses.extend(
+        ['missed_frame', 'submitted_frame', 'main_frame_aborted'])
 
-  frame_max = 0
-  frame_min = 0
-  frame_cumulative = 0
   current_frame_duration = 0
   total_frames = 0
   frame_id_to_start_ts = {}
+  # Will contain tuples of frame_ids and their duration.
+  completed_frame_id_and_duration = []
   for trace in trace_json['traceEvents']:
     if 'PipelineReporter' in trace['name']:
       frame_id = trace['id']
@@ -205,10 +204,8 @@
           continue
         current_frame_duration = trace['ts'] - frame_id_to_start_ts[frame_id]
         total_frames += 1
-        frame_max = max(frame_max, current_frame_duration)
-        frame_min = (min(frame_min, current_frame_duration)
-                     if frame_min else current_frame_duration)
-        frame_cumulative += current_frame_duration
+        completed_frame_id_and_duration.append(
+            (frame_id, current_frame_duration))
         # We are done with this frame_id so remove it from the dict.
         frame_id_to_start_ts.pop(frame_id)
         print '%d (%s with %s): %d' % (
@@ -222,11 +219,28 @@
               frame_id, args['termination_status'])
           frame_id_to_start_ts.pop(frame_id)
 
+  total_completed_frames = len(completed_frame_id_and_duration)
+  if total_completed_frames < 25:
+    raise Exception('Even with 2 loops found only %d frames' %
+                    total_completed_frames)
+
+  # Get frame avg/min/max for the middle 25 frames.
+  start = (total_completed_frames - 25)/2
+  print 'Got %d total completed frames. Using start_index of %d.' % (
+      total_completed_frames, start)
+  frame_max = 0
+  frame_min = 0
+  frame_cumulative = 0
+  for frame_id, duration in completed_frame_id_and_duration[start:start+25]:
+    frame_max = max(frame_max, duration)
+    frame_min = min(frame_min, duration) if frame_min else duration
+    frame_cumulative += duration
+
   perf_results = {}
   perf_results['frame_max_us'] = frame_max
   perf_results['frame_min_us'] = frame_min
-  perf_results['frame_avg_us'] = frame_cumulative/total_frames
-  print 'For %d frames got: %s' % (total_frames, perf_results)
+  perf_results['frame_avg_us'] = frame_cumulative/25
+  print 'For 25 frames got: %s' % perf_results
 
   # Write perf_results to the output json.
   with open(output_json_file, 'w') as f:
diff --git a/tools/lottie-web-perf/lottie-web-perf.html b/tools/lottie-web-perf/lottie-web-perf.html
index 08011d1..1609425 100644
--- a/tools/lottie-web-perf/lottie-web-perf.html
+++ b/tools/lottie-web-perf/lottie-web-perf.html
@@ -21,7 +21,8 @@
     (function () {
       const PATH = '/res/lottie.json';
       const RENDERER = 'svg';
-      const FRAMES = 25;
+      const MAX_FRAMES = 25;
+      const MAX_LOOPS = 2;
 
       // Get total number of frames of the animation from the hash.
       const hash = window.location.hash;
@@ -41,13 +42,20 @@
         },
       });
 
-      const t_rate = 1.0 / (FRAMES - 1);
+      const t_rate = 1.0 / (MAX_FRAMES - 1);
       let frame = 0;
+      let loop = 0;
       const drawFrame = () => {
-        if (frame >= FRAMES) {
-          // These are global variables to talk with puppeteer.
-          window._lottieWebDone = true;
-          return;
+        if (frame >= MAX_FRAMES) {
+          // Reached the end of one loop.
+          loop++;
+          if (loop == MAX_LOOPS) {
+            // These are global variables to talk with puppeteer.
+            window._lottieWebDone = true;
+            return;
+          }
+          // Reset frame to restart the loop.
+          frame = 0;
         }
 
         let t1 = Math.max(Math.min(t_rate * frame, 1.0), 0.0);
@@ -60,6 +68,7 @@
         }
 
         anim.goToAndStop(seekToFrame, true /* isFrame */);
+        console.log("Used seek: " + (seekToFrame/totalFrames));
         frame++;
         window.requestAnimationFrame(drawFrame);
       };
diff --git a/tools/lottie-web-perf/lottie-web-perf.js b/tools/lottie-web-perf/lottie-web-perf.js
index a6bebb1..4334a05 100644
--- a/tools/lottie-web-perf/lottie-web-perf.js
+++ b/tools/lottie-web-perf/lottie-web-perf.js
@@ -122,13 +122,13 @@
     });
 
     await page.goto(targetURL, {
-      timeout: 20000,
+      timeout: 60000,
       waitUntil: 'networkidle0'
     });
 
-    console.log('- Waiting 20s for run to be done.');
+    console.log('- Waiting 60s for run to be done.');
     await page.waitForFunction('window._lottieWebDone === true', {
-      timeout: 20000,
+      timeout: 60000,
     });
 
     // Stop trace.
@@ -137,7 +137,7 @@
     console.log('Timed out while loading or drawing. Either the JSON file was ' +
                 'too big or hit a bug in the player.', e);
     await browser.close();
-    process.exit(0);
+    process.exit(1);
   }
 
   await browser.close();
diff --git a/tools/skottie-wasm-perf/skottie-wasm-perf.html b/tools/skottie-wasm-perf/skottie-wasm-perf.html
index 405310c..9009887 100644
--- a/tools/skottie-wasm-perf/skottie-wasm-perf.html
+++ b/tools/skottie-wasm-perf/skottie-wasm-perf.html
@@ -42,7 +42,8 @@
   // Without flushing, nothing will show up on the screen, although
   // everything else has been executed.
   const shouldFlush = false;
-  const frames = 25;
+  const maxFrames = 25;
+  const maxLoops = 2;
 
   function Bench(CK, json) {
     if (!CK || !json) {
@@ -67,14 +68,22 @@
     // to CPU and add a ck-replaced class to the canvas element.
     window._gpu = CK.gpu && !c.classList.contains('ck-replaced');
 
-    const t_rate = 1.0 / (frames-1);
+    const t_rate = 1.0 / (maxFrames-1);
     let seek = 0;
     let frame = 0;
+    let loop = 0;
     const drawFrame = () => {
-      if (frame >= frames) {
-        // These are global variables to talk with puppeteer.
-        window._skottieDone = true;
-        return;
+      if (frame >= maxFrames) {
+        // Reached the end of one loop.
+        loop++;
+        if (loop == maxLoops) {
+          // These are global variables to talk with puppeteer.
+          window._skottieDone = true;
+          return;
+        }
+        // Reset frame and seek to restart the loop.
+        frame = 0;
+        seek = 0;
       }
 
       animation.seek(seek);
@@ -84,6 +93,7 @@
                        fRight: 1000,
                        fBottom: 1000
                        });
+      console.log("Used seek: " + seek);
       if (shouldFlush) {
         surface.flush();
       }
diff --git a/tools/skottie-wasm-perf/skottie-wasm-perf.js b/tools/skottie-wasm-perf/skottie-wasm-perf.js
index e338ca3..125eab0 100644
--- a/tools/skottie-wasm-perf/skottie-wasm-perf.js
+++ b/tools/skottie-wasm-perf/skottie-wasm-perf.js
@@ -135,13 +135,13 @@
     });
 
     await page.goto(targetURL, {
-      timeout: 20000,
+      timeout: 60000,
       waitUntil: 'networkidle0'
     });
 
-    console.log('Waiting 20s for run to be done');
+    console.log('Waiting 60s for run to be done');
     await page.waitForFunction('window._skottieDone === true', {
-      timeout: 20000,
+      timeout: 60000,
     });
 
     // Stop Trace.
@@ -150,7 +150,7 @@
     console.log('Timed out while loading or drawing. Either the JSON file was ' +
                 'too big or hit a bug.', e);
     await browser.close();
-    process.exit(0);
+    process.exit(1);
   }
 
   await browser.close();