Add parsing and uploading of JMI data to perf dashboard
Pull the JMI log file from the CFM device and parse its data to upload
to Chrome Perf Dashboard.
BUG=chromium:631858
TEST=Tested on a local setup.
Change-Id: Idb0228f283df6e89f2ecc8a2804d00ab26c2fe4e
Reviewed-on: https://chromium-review.googlesource.com/432046
Reviewed-by: Kalin Stoyanov <kalin@chromium.org>
Reviewed-by: Harpreet Grewal <harpreet@chromium.org>
Commit-Queue: Harpreet Grewal <harpreet@chromium.org>
Tested-by: Harpreet Grewal <harpreet@chromium.org>
Trybot-Ready: Harpreet Grewal <harpreet@chromium.org>
diff --git a/server/cros/cfm_jmidata_v3_helper.py b/server/cros/cfm_jmidata_v3_helper.py
new file mode 100644
index 0000000..823fb35
--- /dev/null
+++ b/server/cros/cfm_jmidata_v3_helper.py
@@ -0,0 +1,234 @@
+# Copyright (c) 2017 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import enum, json
+
+from autotest_lib.server.cros import cfm_jmidata_helper_base
+
+# Start index in the JSON object that contains Audio/Video streams related info.
+AV_INDEX = 4
+
+SSRC = u'ssrc'
+GLOBAL = u'global'
+
+AUDIO_INPUT = u'audioInputLevel'
+AUDIO_OUTPUT = u'audioOutputLevel'
+BYTES_RECEIVED = u'bytesReceived'
+BYTES_SENT = u'bytesSent'
+ADAPTATION_CHANGES = u'googAdaptationChanges'
+AVERAGE_ENCODE_TIME = u'googAvgEncodeMs'
+BANDWIDTH_LIMITED_RESOLUTION = u'googBandwidthLimitedResolution'
+CPU_LIMITED_RESOLUTION = u'googCpuLimitedResolution'
+VIDEO_ENCODE_CPU_USAGE = u'googEncodeUsagePercent'
+VIDEO_RECEIVED_FRAME_HEIGHT = u'googFrameHeightReceived'
+VIDEO_RECEIVED_FRAME_WIDTH = u'googFrameWidthReceived'
+FRAMERATE_OUTGOING = u'googFrameRateInput'
+FRAMERATE_RECEIVED = u'googFrameRateReceived'
+FRAMERATE_SENT = u'googFrameRateSent'
+FRAMERATE_DECODED = u'googFrameRateDecoded'
+FRAMERATE_OUTPUT = u'googFrameRateOutput'
+FRAME_WIDTH_SENT = u'googFrameWidthSent'
+FRAME_HEIGHT_SENT = u'googFrameHeightSent'
+FRAMES_DECODED = u'framesDecoded'
+FRAMES_ENCODED = u'framesEncoded'
+VIDEO_PACKETS_LOST = u'packetsLost'
+VIDEO_PACKETS_SENT = u'packetsSent'
+
+
+class CpuUsageType(enum.Enum):
+ BROWSER_CPU = u'browserCpuUsage'
+ GPU_CPU = u'gpuCpuUsage'
+ NUM_PROCESSORS = u'numOfProcessors'
+ NACL_EFFECTS_CPU = u'pluginCpuUsage'
+ RENDERER_CPU = u'tabCpuUsage'
+ TOTAL_CPU = u'totalCpuUsage'
+
+
+class JMIDataV3Helper(cfm_jmidata_helper_base.JMIDataHelperBase):
+ """Helper class for JMI data v3 parsing. This class helps in extracting
+ relevant JMI data from javascript log.
+
+ The class takes javascript file as input and extracts jmidata elements from
+ the file that is internally stored as a list. Whenever we need to extract
+ data i.e. audio received bytes we call a method such as
+ getAudioReceivedDataList. This method converts each string element in the
+ internal list to a json object and retrieves the relevant info from it which
+ is then stored and returned as a list.
+ """
+
+ def __init__(self, log_file_content):
+ super(JMIDataV3Helper, self).__init__(log_file_content, 'jmidatav3')
+
+ def _ExtractAllJMIDataPointsWithKey(self, jmi_type, is_audio, key):
+ """Extracts all values from the data points with the given key."""
+ data_list = []
+ for jmi_data_point in self._jmi_list:
+ json_arr = json.loads(jmi_data_point)
+ for i in range(AV_INDEX, len(json_arr)):
+ if json_arr[i] and jmi_type in json_arr[i]:
+ jmi_obj = json_arr[i][jmi_type]
+ #print jmi_obj
+ this_is_audio = (AUDIO_INPUT in jmi_obj or
+ AUDIO_OUTPUT in jmi_obj)
+ if this_is_audio == is_audio and key in jmi_obj:
+ if (not isinstance(jmi_obj[key], int) and
+ (jmi_obj[key] == 'false' or
+ jmi_obj[key] == 'true')):
+ jmi_obj[key] = 1 if jmi_obj[key] == 'true' else 0
+ data_list.append(jmi_obj[key])
+ return data_list
+
+ def GetAudioReceivedBytesList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=True, key=BYTES_RECEIVED)
+
+ def GetAudioSentBytesList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=True, key=BYTES_SENT)
+
+ def GetAudioReceivedEnergyList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=True, key=AUDIO_OUTPUT)
+
+ def GetAudioSentEnergyList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=True, key=AUDIO_INPUT)
+
+ def GetVideoSentBytesList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=BYTES_SENT)
+
+ def GetVideoReceivedBytesList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=BYTES_RECEIVED)
+
+ def GetVideoIncomingFramerateReceivedList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=FRAMERATE_RECEIVED)
+
+ def GetVideoOutgoingFramerateSentList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=FRAMERATE_SENT)
+
+ def GetVideoIncomingFramerateDecodedList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=FRAMERATE_DECODED)
+
+ def GetVideoIncomingFramerateList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=FRAMERATE_OUTPUT)
+
+ def GetVideoSentFrameWidthList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=FRAME_WIDTH_SENT)
+
+ def GetVideoSentFrameHeightList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=FRAME_HEIGHT_SENT)
+
+ def GetCPULimitedResolutionList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=CPU_LIMITED_RESOLUTION)
+
+ def GetVideoPacketsSentList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=VIDEO_PACKETS_SENT)
+
+ def GetVideoPacketsLostList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=VIDEO_PACKETS_LOST)
+
+ def GetVideoIncomingFramesDecodedList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=FRAMES_DECODED)
+
+ def GetVideoOutgoingFramesEncodedList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=FRAMES_ENCODED)
+
+ def GetVideoAdaptationChangeList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=ADAPTATION_CHANGES)
+
+ def GetVideoEncodeTimeList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=AVERAGE_ENCODE_TIME)
+
+ def GetBandwidthLimitedResolutionList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=BANDWIDTH_LIMITED_RESOLUTION)
+
+ def GetVideoReceivedFrameHeightList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=VIDEO_RECEIVED_FRAME_HEIGHT)
+
+ def GetVideoOutgoingFramerateInputList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=FRAMERATE_OUTGOING)
+
+ def GetVideoReceivedFrameWidthList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=VIDEO_RECEIVED_FRAME_WIDTH)
+
+ def GetVideoEncodeCpuUsagePercentList(self):
+ return self._ExtractAllJMIDataPointsWithKey(
+ jmi_type=SSRC, is_audio=False, key=VIDEO_ENCODE_CPU_USAGE)
+
+ def GetNumberOfActiveIncomingVideoStreams(self):
+ """Retrieve number of active incoming video streams."""
+ if not self._jmi_list:
+ # JMI data hasn't started populating yet.
+ return 0
+
+ num_video_streams = []
+
+ # If JMI data has started getting populated and has video stream data.
+ for jmi_data_point in self._jmi_list:
+ json_arr = json.loads(jmi_data_point)
+ video_streams = 0
+ for i in range(AV_INDEX, len(json_arr)):
+ if json_arr[i] and SSRC in json_arr[i]:
+ ssrc_obj = json_arr[i][SSRC]
+ is_audio = ('audioInputLevel' in ssrc_obj or
+ 'audioOutputLevel' in ssrc_obj)
+ is_incoming = 'bytesReceived' in ssrc_obj
+ frame_rate_received = 'googFrameRateReceived' in ssrc_obj
+ if ssrc_obj['mediaType'] == 'video' and \
+ frame_rate_received:
+ frame_rate = ssrc_obj['googFrameRateReceived']
+ if (is_incoming and not is_audio) and \
+ frame_rate != 0:
+ video_streams += 1
+ num_video_streams.append(video_streams)
+ return num_video_streams
+
+ def GetCpuUsageList(self, cpu_type):
+ """Retrieves cpu usage data from JMI data.
+
+ @param cpu_type: Enum of type CpuUsageType.
+ @returns List containing CPU usage data.
+ """
+ data_list = []
+ for jmi_data_point in self._jmi_list:
+ json_arr = json.loads(jmi_data_point)
+ for i in range(AV_INDEX, len(json_arr)):
+ if json_arr[i] and GLOBAL in json_arr[i]:
+ global_obj = json_arr[i][GLOBAL]
+ # Some values in JMIDataV3 are set to 'null'.
+ if cpu_type.value == u'numOfProcessors':
+ return global_obj[cpu_type.value]
+ elif (cpu_type.value in global_obj and
+ self.IsFloat(global_obj[cpu_type.value])):
+ data_list.append(float(global_obj[cpu_type.value]))
+ return data_list
+
+ def GetCpuUsageType(self):
+ return CpuUsageType
+
+ def IsFloat(self, value):
+ try:
+ float(value)
+ return True
+ except TypeError:
+ return False