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