EventPlot.py revision f9c81a71180f1092bdcc71b5a6ed091f9b7fd950
1# Copyright 2015-2016 ARM Limited 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14# 15 16""" 17The EventPlot is used to represent Events with two characteristics: 18 19 - A name, which determines the colour on the plot 20 - A lane, which determines the lane in which the event occurred 21 22In the case of a cpu residency plot, the term lane can be equated to 23a CPU and the name attribute can be the PID of the task 24""" 25 26from trappy.plotter import AttrConf 27import uuid 28import json 29import os 30from trappy.plotter.AbstractDataPlotter import AbstractDataPlotter 31from trappy.plotter import IPythonConf 32from copy import deepcopy 33 34if not IPythonConf.check_ipython(): 35 raise ImportError("Ipython Environment not Found") 36 37from IPython.display import display, HTML 38# pylint: disable=R0201 39# pylint: disable=R0921 40 41 42class EventPlot(AbstractDataPlotter): 43 """ 44 Input Data should be of the format 45 :: 46 47 { "<name1>" : [ 48 [event_start, event_end, lane], 49 . 50 . 51 [event_start, event_end, lane], 52 ], 53 . 54 . 55 . 56 57 "<nameN>" : [ 58 [event_start, event_end, lane], 59 . 60 . 61 [event_start, event_end, lane], 62 ], 63 } 64 65 :param data: Input Data 66 :type data: dict 67 68 :param keys: List of unique names in the data dictionary 69 :type keys: list 70 71 :param domain: Domain of the event data 72 :type domain: tuple 73 74 :param lane_prefix: A string prefix to be used to name each lane 75 :type lane_prefix: str 76 77 :param num_lanes: Total number of expected lanes 78 :type num_lanes: int 79 80 :param summary: Show a mini plot below the main plot with an 81 overview of where your current view is with respect to the 82 whole trace 83 :type summary: bool 84 85 :param stride: Stride can be used if the trace is very large. 86 It results in sampled rendering 87 :type stride: bool 88 89 :param lanes: The sorted order of lanes 90 :type lanes: list 91 92 :param color_map: A mapping between events and colours 93 :: 94 { "<name1>" : "colour1", 95 . 96 . 97 . 98 "<nameN>" : "colourN" 99 } 100 101 Colour string can be: 102 103 - Colour names (supported colours are listed in 104 https://www.w3.org/TR/SVG/types.html#ColorKeywords) 105 106 - HEX representation of colour, like #FF0000 for "red", #008000 for 107 "green", #0000FF for "blue" and so on 108 109 :type color_map: dict 110 """ 111 112 def __init__( 113 self, 114 data, 115 keys, 116 domain, 117 lane_prefix="Lane: ", 118 num_lanes=0, 119 summary=True, 120 stride=False, 121 lanes=None, 122 color_map=None): 123 124 _data = deepcopy(data) 125 self._html = [] 126 self._fig_name = self._generate_fig_name() 127 # Function to get the average duration of each event 128 avgFunc = lambda x: sum([(evt[1] - evt[0]) for evt in x]) / float(len(x) + 1) 129 avg = {k: avgFunc(v) for k, v in data.iteritems()} 130 # Filter keys with zero average time 131 keys = filter(lambda x : avg[x] != 0, avg) 132 graph = {} 133 graph["data"] = _data 134 graph["lanes"] = self._get_lanes(lanes, lane_prefix, num_lanes, _data) 135 graph["xDomain"] = domain 136 graph["keys"] = sorted(keys, key=lambda x: avg[x], reverse=True) 137 graph["showSummary"] = summary 138 graph["stride"] = AttrConf.EVENT_PLOT_STRIDE 139 graph["colorMap"] = color_map 140 self._data = json.dumps(graph) 141 142 143 144 # Initialize the HTML, CSS and JS Components 145 self._add_css() 146 self._init_html() 147 148 def view(self): 149 """Views the Graph Object""" 150 151 # Defer installation of IPython components 152 # to the .view call to avoid any errors at 153 # when importing the module. This facilitates 154 # the importing of the module from outside 155 # an IPython notebook 156 IPythonConf.iplot_install("EventPlot") 157 display(HTML(self.html())) 158 159 def savefig(self, path): 160 """Save the plot in the provided path 161 162 .. warning:: Not Implemented for :mod:`trappy.plotter.EventPlot` 163 """ 164 165 raise NotImplementedError( 166 "Save is not currently implemented for EventPlot") 167 168 def _get_lanes(self, 169 input_lanes, 170 lane_prefix, 171 num_lanes, 172 data): 173 """Populate the lanes for the plot""" 174 175 # If the user has specified lanes explicitly 176 lanes = [] 177 if input_lanes: 178 lane_map = {} 179 for idx, lane in enumerate(input_lanes): 180 lane_map[lane] = idx 181 182 for name in data: 183 for event in data[name]: 184 lane = event[2] 185 186 try: 187 event[2] = lane_map[lane] 188 except KeyError: 189 raise RuntimeError("Invalid Lane %s" % lane) 190 191 for idx, lane in enumerate(input_lanes): 192 lanes.append({"id": idx, "label": lane}) 193 194 else: 195 196 if not num_lanes: 197 raise RuntimeError("Either lanes or num_lanes must be specified") 198 199 for idx in range(num_lanes): 200 lanes.append({"id": idx, "label": "{}{}".format(lane_prefix, idx)}) 201 202 return lanes 203 204 def _generate_fig_name(self): 205 """Generate a unqiue name for the figure""" 206 207 fig_name = "fig_" + uuid.uuid4().hex 208 return fig_name 209 210 def _init_html(self): 211 """Initialize HTML for the plot""" 212 div_js = '' 213 for url in [IPythonConf.D3_PLOTTER_URL, IPythonConf.D3_TIP_URL]: 214 div_js += '<!-- TRAPPY_PUBLISH_SOURCE_LIB = "{}" -->\n'.format(url) 215 216 div_js += """ 217 <script> 218 /* TRAPPY_PUBLISH_IMPORT = "plotter/js/EventPlot.js" */ 219 /* TRAPPY_PUBLISH_REMOVE_START */ 220 var req = require.config( { 221 222 paths: { 223 224 "EventPlot": '""" + IPythonConf.add_web_base("plotter_scripts/EventPlot/EventPlot") + """', 225 "d3-tip": '""" + IPythonConf.add_web_base("plotter_scripts/EventPlot/d3.tip.v0.6.3") + """', 226 "d3-plotter": '""" + IPythonConf.add_web_base("plotter_scripts/EventPlot/d3.min") + """' 227 }, 228 waitSeconds: 15, 229 shim: { 230 "d3-plotter" : { 231 "exports" : "d3" 232 }, 233 "d3-tip": ["d3-plotter"], 234 "EventPlot": { 235 236 "deps": ["d3-tip", "d3-plotter" ], 237 "exports": "EventPlot" 238 } 239 } 240 }); 241 /* TRAPPY_PUBLISH_REMOVE_STOP */ 242 """ 243 244 div_js += """ 245 req(["require", "EventPlot"], function() { /* TRAPPY_PUBLISH_REMOVE_LINE */ 246 EventPlot.generate('""" + self._fig_name + "', '" + IPythonConf.add_web_base("") + "', " + self._data + """); 247 }); /* TRAPPY_PUBLISH_REMOVE_LINE */ 248 </script> 249 """ 250 251 self._html.append( 252 '<div id="{}" class="eventplot">\n{}</div>'.format(self._fig_name, 253 div_js)) 254 255 def _add_css(self): 256 """Append the CSS to the HTML code generated""" 257 258 base_dir = os.path.dirname(os.path.realpath(__file__)) 259 css_file = os.path.join(base_dir, "css/EventPlot.css") 260 self._html.append("<style>") 261 262 with open(css_file, 'r') as css_fh: 263 self._html += [l[:-1] for l in css_fh.readlines()] 264 265 self._html.append("</style>") 266 267 def html(self): 268 """Return a Raw HTML string for the plot""" 269 270 return "\n".join(self._html) 271