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())
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 = {}
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()))
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)))
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
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())
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()