Beispiel #1
0
def autodoc_process_analysis_plots(app, what, name, obj, options, lines, plot_conf):
    if what != 'method':
        return

    plot_methods = set(itertools.chain.from_iterable(
        subclass.get_plot_methods()
        for subclass in get_subclasses(TraceAnalysisBase)
    ))

    if obj not in plot_methods:
        return

    plot_conf = plot_conf['plots']

    default_spec = plot_conf.get('default', {})
    spec = plot_conf.get(obj.__qualname__, {})
    spec = {**default_spec, **spec}
    kwargs = spec.get('kwargs', {})
    trace = spec['trace']

    if spec.get('hide'):
        return

    print('Generating plot for {}'.format(obj.__qualname__))
    rst_figure = TraceAnalysisBase.call_on_trace(obj, trace, {
        'output': 'rst',
        'always_save': False,
        # avoid memory leaks
        'interactive': False,
        **kwargs
    })
    rst_figure = '\n:Example plot:\n\n{}'.format(rst_figure)
    lines.extend(rst_figure.splitlines())
Beispiel #2
0
 def __init__(self, trace, params=None):
     self._preset_params = params or {}
     self.trace = trace
     # Get the list once when the proxy is built, since we know all classes
     # will have had a chance to get registered at that point
     self._class_map = TraceAnalysisBase.get_analysis_classes()
     self._instance_map = {}
Beispiel #3
0
 def get_all_events(cls):
     """
     Returns the set of all events used by any of the registered analysis.
     """
     return set(
         itertools.chain.from_iterable(
             cls.get_all_events()
             for cls in TraceAnalysisBase.get_analysis_classes().values()))
Beispiel #4
0
    def get_all_events(cls):
        """
        Returns the set of all events used by any of the registered analysis.
        """
        def predicate(f):
            return callable(f) and hasattr(f, 'used_events')

        return set(
            itertools.chain.from_iterable(
                attr.used_events.get_all_events()
                for cls in TraceAnalysisBase.get_analysis_classes().values()
                for name, attr in inspect.getmembers(cls,
                                                     predicate=predicate)))
Beispiel #5
0
def get_plots_map():
    plots_map = {}
    for name, cls in TraceAnalysisBase.get_analysis_classes().items():

        methods = [
            meth for meth in cls.get_plot_methods()
            # Method that need extra arguments are not usable by this
            # script
            if meth_usable_args(meth)
        ]

        if methods:
            plots_map[name] = {
                make_meth_name(name, meth): meth
                for meth in methods
            }
    return plots_map
Beispiel #6
0
def autodoc_process_analysis_plots(app, what, name, obj, options, lines,
                                   plot_conf):
    if what != 'method':
        return

    plot_methods = set(
        itertools.chain.from_iterable(
            subclass.get_plot_methods()
            for subclass in get_subclasses(TraceAnalysisBase)))

    if obj not in plot_methods:
        return

    plot_conf = plot_conf['plots']

    default_spec = plot_conf.get('default', {})
    spec = plot_conf.get(obj.__qualname__, {})
    spec = {**default_spec, **spec}
    kwargs = spec.get('kwargs', {})
    trace = spec['trace']

    if spec.get('hide'):
        return

    print(f'Generating plot for {obj.__qualname__}')

    # Suppress deprecation warnings so we can still have them in the doc
    with warnings.catch_warnings():
        warnings.simplefilter("ignore", category=DeprecationWarning)

        rst_figure = TraceAnalysisBase.call_on_trace(
            obj, trace, {
                'backend': 'bokeh',
                'output': 'sphinx-rst',
                'interactive': False,
                **kwargs
            })
    rst_figure = f'\n:Example plot:\n\n{rst_figure}'
    lines.extend(rst_figure.splitlines())
Beispiel #7
0
def main(argv=None):
    if argv is None:
        argv = sys.argv[1:]

    plots_map = get_plots_map()
    analysis_nice_name_map = {
        get_analysis_nice_name(name): name
        for name in plots_map.keys()
    }

    parser = argparse.ArgumentParser(
        description="""
CLI for LISA analysis plots and reports from traces.

Available plots:

{}

""".format(get_analysis_listing(plots_map)),
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )

    parser.add_argument(
        'trace',
        help='trace-cmd trace.dat, or systrace file',
    )

    parser.add_argument(
        '--normalize-time',
        action='store_true',
        help=
        'Normalize the time in the plot, i.e. start at 0 instead of uptime timestamp',
    )

    parser.add_argument(
        '--plot',
        nargs=2,
        action='append',
        default=[],
        metavar=('PLOT', 'OUTPUT_PATH'),
        help=
        'Create the given plot. If OUTPUT_PATH is "interactive", an interactive window will be used',
    )

    parser.add_argument(
        '--plot-analysis',
        nargs=3,
        action='append',
        default=[],
        metavar=('ANALYSIS', 'OUTPUT_FOLDER_PATH', 'FORMAT'),
        help='Create all the plots of the given analysis',
    )

    parser.add_argument(
        '--plot-all',
        nargs=2,
        metavar=('OUTPUT_FOLDER_PATH', 'FORMAT'),
        help='Create all the plots in the given folder',
    )

    parser.add_argument(
        '--best-effort',
        action='store_true',
        help=
        'Try to generate as many of the requested plots as possible without early termination.',
    )

    parser.add_argument(
        '--window',
        nargs=2,
        type=float,
        metavar=('BEGIN', 'END'),
        help='Only plot data between BEGIN and END times',
    )

    parser.add_argument(
        '-X',
        '--option',
        nargs=2,
        action='append',
        default=[],
        metavar=('OPTION_NAME', 'VALUE'),
        help=
        'Pass extra parameters to plot methods, e.g. "-X cpu 1". Mismatching names are ignored.',
    )

    parser.add_argument(
        '--matplotlib-backend',
        default='GTK3Agg',
        help='matplotlib backend to use for interactive window',
    )

    parser.add_argument(
        '--plat-info',
        help='Platform information, necessary for some plots',
    )

    parser.add_argument(
        '--xkcd',
        action='store_true',
        help='Graphs will look like XKCD plots',
    )

    args = parser.parse_args(argv)

    flat_plot_map = {
        plot_name: meth
        for analysis_name, plot_list in plots_map.items()
        for plot_name, meth in plot_list.items()
    }

    if args.plat_info:
        plat_info = PlatformInfo.from_yaml_map(args.plat_info)
    else:
        plat_info = None

    if args.plot_all:
        folder, fmt = args.plot_all
        plot_analysis_spec_list = [(get_analysis_nice_name(analysis_name),
                                    folder, fmt)
                                   for analysis_name in plots_map.keys()]
    else:
        plot_analysis_spec_list = []

    plot_analysis_spec_list.extend(args.plot_analysis)

    plot_spec_list = [(plot_name,
                       os.path.join(folder, '{}.{}'.format(plot_name, fmt)))
                      for analysis_name, folder, fmt in plot_analysis_spec_list
                      for plot_name, meth in plots_map[
                          analysis_nice_name_map[analysis_name]].items()]

    plot_spec_list.extend(args.plot)

    # Build minimal event list to speed up trace loading time
    plot_methods = set()
    for plot_name, file_path in plot_spec_list:
        try:
            f = flat_plot_map[plot_name]
        except KeyError:
            error('Unknown plot "{}", see --help'.format(plot_name))

        plot_methods.add(f)

    # If best effort is used, we don't want to trigger exceptions ahead of
    # time. Let it fail for individual plot methods instead, so the trace can
    # be used for the other events
    if args.best_effort:
        events = None
    else:
        events = set()
        for f in plot_methods:
            with contextlib.suppress(AttributeError):
                events.update(f.used_events.get_all_events())

        events = sorted(events)
        print('Parsing trace events: {}'.format(', '.join(events)))

    trace = Trace(args.trace,
                  plat_info=plat_info,
                  events=events,
                  normalize_time=args.normalize_time,
                  write_swap=True)
    if args.window:
        window = args.window

        def clip(l, x, r):
            if x < l:
                return l
            elif x > r:
                return r
            else:
                return x

        window = (
            clip(trace.window[0], window[0], trace.window[1]),
            clip(trace.window[0], window[1], trace.window[1]),
        )
        # There is no overlap between trace and user window, reset to trace
        # window
        if window[0] == window[1]:
            print(
                'Window {} does not overlap with trace time range, maybe you forgot --normalize-time ?'
                .format(tuple(args.window)))
            window = trace.window

        trace = trace.get_view(window)

    for plot_name, file_path in sorted(plot_spec_list):
        interactive = file_path == 'interactive'
        f = flat_plot_map[plot_name]
        if interactive:
            matplotlib.use(args.matplotlib_backend)
            file_path = None
        else:
            dirname = os.path.dirname(file_path)
            if dirname:
                os.makedirs(dirname, exist_ok=True)

        kwargs = make_plot_kwargs(f,
                                  file_path,
                                  interactive=interactive,
                                  extra_options=args.option)

        xkcd_cm = plt.xkcd() if args.xkcd else nullcontext()
        with handle_plot_excep(exit_on_error=not args.best_effort):
            with xkcd_cm:
                TraceAnalysisBase.call_on_trace(f, trace, kwargs)

        if interactive:
            plt.show()