Beispiel #1
0
def _PullTraces(controllers, output, compress, write_json):
    ui.PrintMessage('Downloading...', eol='')
    trace_files = [controller.PullTrace() for controller in controllers]
    trace_files = [trace for trace in trace_files if trace]
    if not trace_files:
        ui.PrintMessage('No results')
        return ''

    trace_files = output_generator.MergeTraceFilesIfNeeded(trace_files)
    if not write_json:
        print 'Writing trace HTML'
        html_file = os.path.splitext(trace_files[0])[0] + '.html'
        trace_results = _PrepareTracesForOutput(trace_files)
        result = output_generator.GenerateHTMLOutput(trace_results, html_file)
        print '\nWrote file://%s\n' % result
        trace_files = [html_file]
    if compress and len(trace_files) == 1:
        result = output or trace_files[0] + '.gz'
        util.CompressFile(trace_files[0], result)
    elif len(trace_files) > 1:
        result = (output
                  or 'chrome-combined-trace-%s.zip' % util.GetTraceTimestamp())
        util.ArchiveFiles(trace_files, result)
    elif output:
        result = output
        shutil.move(trace_files[0], result)
    else:
        result = trace_files[0]

    return result
Beispiel #2
0
def CaptureProfile(options,
                   interval,
                   modules,
                   output=None,
                   compress=False,
                   write_json=False):
    """Records a profiling trace saves the result to a file.

  Args:
    options: Command line options.
    interval: Time interval to capture in seconds. An interval of None (or 0)
        continues tracing until stopped by the user.
    modules: The list of modules to initialize the tracing controller with.
    output: Output file name or None to use an automatically generated name.
    compress: If True, the result will be compressed either with gzip or zip
        depending on the number of captured subtraces.
    write_json: If True, prefer JSON output over HTML.

  Returns:
    Path to saved profile.
  """
    agents_with_config = tracing_controller.CreateAgentsWithConfig(
        options, modules)
    if chrome_startup_tracing_agent in modules:
        controller_config = tracing_controller.GetChromeStartupControllerConfig(
            options)
    else:
        controller_config = tracing_controller.GetControllerConfig(options)
    controller = tracing_controller.TracingController(agents_with_config,
                                                      controller_config)
    try:
        result = controller.StartTracing()
        trace_type = controller.GetTraceType()
        if not result:
            ui.PrintMessage('Trace starting failed.')
        if interval:
            ui.PrintMessage(
                ('Capturing %d-second %s. Press Enter to stop early...' %
                 (interval, trace_type)),
                eol='')
            ui.WaitForEnter(interval)
        else:
            ui.PrintMessage('Capturing %s. Press Enter to stop...' %
                            trace_type,
                            eol='')
            raw_input()

        ui.PrintMessage('Stopping...')
        all_results = controller.StopTracing()
    finally:
        if interval:
            ui.PrintMessage('done')

    return _GetResults(all_results, controller, output, compress, write_json,
                       interval)
Beispiel #3
0
def _PullTraces(controllers, output, compress, write_json):
    ui.PrintMessage('Downloading...', eol='')
    trace_files = [controller.PullTrace() for controller in controllers]
    trace_files = [trace for trace in trace_files if trace]
    if not trace_files:
        ui.PrintMessage('No results')
        return []
    result = trace_packager.PackageTraces(trace_files,
                                          output=output,
                                          compress=compress,
                                          write_json=write_json)
    ui.PrintMessage('done')
    ui.PrintMessage('Trace written to file://%s' % os.path.abspath(result))
    return result
Beispiel #4
0
    def PullTrace(self):
        symfs_dir = os.path.join(tempfile.gettempdir(),
                                 os.path.expandvars('$USER-perf-symfs'))
        if not os.path.exists(symfs_dir):
            os.makedirs(symfs_dir)
        required_libs = set()

        # Download the recorded perf profile.
        perf_profile = self._perf_instance.PullResult(symfs_dir)
        required_libs = \
            android_profiling_helper.GetRequiredLibrariesForPerfProfile(
                perf_profile)
        if not required_libs:
            logging.warning(
                'No libraries required by perf trace. Most likely there '
                'are no samples in the trace.')

        # Build a symfs with all the necessary libraries.
        kallsyms = android_profiling_helper.CreateSymFs(self._device,
                                                        symfs_dir,
                                                        required_libs,
                                                        use_symlinks=False)
        perfhost_path = binary_manager.FetchPath(
            android_profiling_helper.GetPerfhostName(), 'x86_64', 'linux')

        ui.PrintMessage('\nNote: to view the profile in perf, run:')
        ui.PrintMessage('  ' + self._GetInteractivePerfCommand(
            perfhost_path, perf_profile, symfs_dir, required_libs, kallsyms))

        # Convert the perf profile into JSON.
        perf_script_path = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), 'third_party',
            'perf_to_tracing.py')
        json_file_name = os.path.basename(perf_profile)
        with open(os.devnull, 'w') as dev_null, \
            open(json_file_name, 'w') as json_file:
            cmd = [
                perfhost_path, 'script', '-s', perf_script_path, '-i',
                perf_profile, '--symfs', symfs_dir, '--kallsyms', kallsyms
            ]
            if subprocess.call(cmd, stdout=json_file, stderr=dev_null):
                logging.warning(
                    'Perf data to JSON conversion failed. The result will '
                    'not contain any perf samples. You can still view the '
                    'perf data manually as shown above.')
                return None

        return json_file_name
Beispiel #5
0
def _GetResults(trace_results, controller, output, compress, write_json,
                interval):
    ui.PrintMessage('Downloading...')

    # Wait for the trace file to get written.
    time.sleep(1)

    for agent in controller.get_child_agents:
        if isinstance(agent, chrome_tracing_agent.ChromeTracingAgent):
            time.sleep(interval / 4)

    # Ignore the systraceController because it will not contain any results,
    # instead being in charge of collecting results.
    trace_results = [
        x for x in controller.all_results
        if not (x.source_name == 'systraceController')
    ]

    if not trace_results:
        ui.PrintMessage('No results')
        return ''

    result = None
    trace_results = output_generator.MergeTraceResultsIfNeeded(trace_results)
    if not write_json:
        ui.PrintMessage('Writing trace HTML...')
        html_file = output or trace_results[0].source_name + '.html'
        result = output_generator.GenerateHTMLOutput(trace_results, html_file)
        ui.PrintMessage('\nWrote file://%s' % result)
    elif compress and len(trace_results) == 1:
        result = output or trace_results[0].source_name + '.gz'
        util.WriteDataToCompressedFile(trace_results[0].raw_data, result)
    elif len(trace_results) > 1:
        result = (output
                  or 'chrome-combined-trace-%s.zip' % util.GetTraceTimestamp())
        util.ArchiveData(trace_results, result)
    elif output:
        result = output
        with open(result, 'wb') as f:
            f.write(trace_results[0].raw_data)
    else:
        result = trace_results[0].source_name
        with open(result, 'wb') as f:
            f.write(trace_results[0].raw_data)

    return result
Beispiel #6
0
def _GetResults(agents, output, compress, write_json, interval):
    ui.PrintMessage('Downloading...', eol='')

    # Wait for the trace file to get written.
    time.sleep(1)

    trace_results = []
    for agent in agents:
        if isinstance(agent, chrome_tracing_agent.ChromeTracingAgent):
            time.sleep(interval / 4)
        trace_results.append(agent.GetResults())

    if not trace_results:
        ui.PrintMessage('No results')
        return ''

    result = None
    trace_results = output_generator.MergeTraceResultsIfNeeded(trace_results)
    if not write_json:
        print 'Writing trace HTML'
        html_file = trace_results[0].source_name + '.html'
        result = output_generator.GenerateHTMLOutput(trace_results, html_file)
        print '\nWrote file://%s\n' % result
    elif compress and len(trace_results) == 1:
        result = output or trace_results[0].source_name + '.gz'
        util.WriteDataToCompressedFile(trace_results[0].raw_data, result)
    elif len(trace_results) > 1:
        result = (output
                  or 'chrome-combined-trace-%s.zip' % util.GetTraceTimestamp())
        util.ArchiveData(trace_results, result)
    elif output:
        result = output
        with open(result, 'wb') as f:
            f.write(trace_results[0].raw_data)
    else:
        result = trace_results[0].source_name
        with open(result, 'wb') as f:
            f.write(trace_results[0].raw_data)

    return result
Beispiel #7
0
def CaptureProfile(agents,
                   interval,
                   output=None,
                   compress=False,
                   write_json=False):
    """Records a profiling trace saves the result to a file.

  Args:
    agents: List of tracing agents.
    interval: Time interval to capture in seconds. An interval of None (or 0)
        continues tracing until stopped by the user.
    output: Output file name or None to use an automatically generated name.
    compress: If True, the result will be compressed either with gzip or zip
        depending on the number of captured subtraces.
    write_json: If True, prefer JSON output over HTML.

  Returns:
    Path to saved profile.
  """
    trace_type = ' + '.join(map(str, agents))
    try:
        _StartTracing(agents)
        if interval:
            ui.PrintMessage(
                ('Capturing %d-second %s. Press Enter to stop early...' %
                 (interval, trace_type)),
                eol='')
            ui.WaitForEnter(interval)
        else:
            ui.PrintMessage('Capturing %s. Press Enter to stop...' %
                            trace_type,
                            eol='')
            raw_input()
    finally:
        _StopTracing(agents)
    if interval:
        ui.PrintMessage('done')

    return _GetResults(agents, output, compress, write_json, interval)
def main():
    parser = _CreateOptionParser()
    options, _ = parser.parse_args()

    if options.verbose:
        logging.getLogger().setLevel(logging.DEBUG)

    devices = device_utils.DeviceUtils.HealthyDevices()
    if len(devices) != 1:
        logging.error('Exactly 1 device must be attached.')
        return 1
    device = devices[0]
    package_info = profiler.GetSupportedBrowsers()[options.browser]

    if options.systrace_categories in ['list', 'help']:
        ui.PrintMessage('\n'.join(
            systrace_controller.SystraceController.GetCategories(device)))
        return 0
    systrace_categories = (options.systrace_categories.split(',')
                           if options.systrace_categories else [])
    enabled_controllers = []
    # Enable the systrace and chrome controller. The systrace controller should go
    # first because otherwise the resulting traces miss early systrace data.
    if systrace_categories:
        enabled_controllers.append(
            systrace_controller.SystraceController(device, systrace_categories,
                                                   False))
    enabled_controllers.append(
        chrome_startup_controller.ChromeStartupTracingController(
            device, package_info, options.cold, options.url))
    if options.output:
        options.output = os.path.expanduser(options.output)
    result = profiler.CaptureProfile(enabled_controllers,
                                     options.time,
                                     output=options.output,
                                     compress=options.compress,
                                     write_json=options.json)
    if options.view:
        if sys.platform == 'darwin':
            os.system('/usr/bin/open %s' % os.path.abspath(result))
        else:
            webbrowser.open(result)
Beispiel #9
0
def main():
    parser = _CreateOptionParser()
    options, _args = parser.parse_args()  # pylint: disable=unused-variable
    if options.trace_cc:
        parser.error("""--trace-cc is deprecated.

For basic jank busting uses, use  --trace-frame-viewer
For detailed study of ubercompositor, pass --trace-ubercompositor.

When in doubt, just try out --trace-frame-viewer.
""")

    if options.verbose:
        logging.getLogger().setLevel(logging.DEBUG)

    if not options.device_serial_number:
        devices = [
            a.GetDeviceSerial() for a in adb_wrapper.AdbWrapper.Devices()
        ]
        if len(devices) == 0:
            raise RuntimeError('No ADB devices connected.')
        elif len(devices) >= 2:
            raise RuntimeError(
                'Multiple devices connected, serial number required')
        options.device_serial_number = devices[0]
    device = device_utils.DeviceUtils.HealthyDevices(
        device_arg=options.device_serial_number)[0]
    package_info = util.get_supported_browsers()[options.browser]

    options.device = device
    options.package_info = package_info

    # Include Chrome categories by default in profile_chrome.
    if not options.chrome_categories:
        options.chrome_categories = chrome_tracing_agent.DEFAULT_CHROME_CATEGORIES

    if options.chrome_categories in ['list', 'help']:
        ui.PrintMessage('Collecting record categories list...', eol='')
        record_categories = []
        disabled_by_default_categories = []
        record_categories, disabled_by_default_categories = \
            chrome_tracing_agent.ChromeTracingAgent.GetCategories(
                device, package_info)

        ui.PrintMessage('done')
        ui.PrintMessage('Record Categories:')
        ui.PrintMessage('\n'.join('\t%s' % item \
            for item in sorted(record_categories)))

        ui.PrintMessage('\nDisabled by Default Categories:')
        ui.PrintMessage('\n'.join('\t%s' % item \
            for item in sorted(disabled_by_default_categories)))

        return 0

    if options.atrace_categories in ['list', 'help']:
        atrace_agent.list_categories(atrace_agent.get_config(options))
        print '\n'
        return 0

    if (perf_tracing_agent.PerfProfilerAgent.IsSupported()
            and options.perf_categories in ['list', 'help']):
        ui.PrintMessage('\n'.join(
            perf_tracing_agent.PerfProfilerAgent.GetCategories(device)))
        return 0

    if not options.trace_time and not options.continuous:
        ui.PrintMessage(
            'Time interval or continuous tracing should be specified.')
        return 1

    if (options.chrome_categories and options.atrace_categories
            and 'webview' in options.atrace_categories):
        logging.warning('Using the "webview" category in atrace together with '
                        'Chrome tracing results in duplicate trace events.')

    if options.output_file:
        options.output_file = os.path.expanduser(options.output_file)
    result = profiler.CaptureProfile(
        options,
        options.trace_time if not options.continuous else 0,
        _PROFILE_CHROME_AGENT_MODULES,
        output=options.output_file,
        compress=options.compress,
        write_json=options.write_json)
    if options.view:
        if sys.platform == 'darwin':
            os.system('/usr/bin/open %s' % os.path.abspath(result))
        else:
            webbrowser.open(result)
Beispiel #10
0
def main():
    parser = _CreateOptionParser()
    options, _args = parser.parse_args()
    if options.trace_cc:
        parser.parse_error("""--trace-cc is deprecated.

For basic jank busting uses, use  --trace-frame-viewer
For detailed study of ubercompositor, pass --trace-ubercompositor.

When in doubt, just try out --trace-frame-viewer.
""")

    if options.verbose:
        logging.getLogger().setLevel(logging.DEBUG)

    devices = device_utils.DeviceUtils.HealthyDevices()
    device = None
    if options.device:
        device = next((d for d in devices if d == options.device), None)
    elif len(devices) == 1:
        device = devices[0]

    if not device:
        parser.error('Use -d/--device to select a device:\n' +
                     '\n'.join(devices))
    package_info = profiler.GetSupportedBrowsers()[options.browser]

    if options.chrome_categories in ['list', 'help']:
        ui.PrintMessage('Collecting record categories list...', eol='')
        record_categories = []
        disabled_by_default_categories = []
        record_categories, disabled_by_default_categories = \
            chrome_controller.ChromeTracingController.GetCategories(
                device, package_info)

        ui.PrintMessage('done')
        ui.PrintMessage('Record Categories:')
        ui.PrintMessage('\n'.join('\t%s' % item \
            for item in sorted(record_categories)))

        ui.PrintMessage('\nDisabled by Default Categories:')
        ui.PrintMessage('\n'.join('\t%s' % item \
            for item in sorted(disabled_by_default_categories)))

        return 0

    if options.systrace_categories in ['list', 'help']:
        ui.PrintMessage('\n'.join(
            systrace_controller.SystraceController.GetCategories(device)))
        return 0

    if (perf_controller.PerfProfilerController.IsSupported()
            and options.perf_categories in ['list', 'help']):
        ui.PrintMessage('\n'.join(
            perf_controller.PerfProfilerController.GetCategories(device)))
        return 0

    if not options.time and not options.continuous:
        ui.PrintMessage(
            'Time interval or continuous tracing should be specified.')
        return 1

    chrome_categories = _ComputeChromeCategories(options)
    systrace_categories = _ComputeSystraceCategories(options)
    perf_categories = _ComputePerfCategories(options)

    if chrome_categories and 'webview' in systrace_categories:
        logging.warning(
            'Using the "webview" category in systrace together with '
            'Chrome tracing results in duplicate trace events.')

    enabled_controllers = []
    if chrome_categories:
        enabled_controllers.append(
            chrome_controller.ChromeTracingController(device, package_info,
                                                      chrome_categories,
                                                      options.ring_buffer,
                                                      options.trace_memory))
    if systrace_categories:
        enabled_controllers.append(
            systrace_controller.SystraceController(device, systrace_categories,
                                                   options.ring_buffer))

    if perf_categories:
        enabled_controllers.append(
            perf_controller.PerfProfilerController(device, perf_categories))

    if options.ddms:
        enabled_controllers.append(
            ddms_controller.DdmsController(device, package_info))

    if not enabled_controllers:
        ui.PrintMessage('No trace categories enabled.')
        return 1

    if options.output:
        options.output = os.path.expanduser(options.output)
    result = profiler.CaptureProfile(
        enabled_controllers,
        options.time if not options.continuous else 0,
        output=options.output,
        compress=options.compress,
        write_json=options.json)
    if options.view:
        if sys.platform == 'darwin':
            os.system('/usr/bin/open %s' % os.path.abspath(result))
        else:
            webbrowser.open(result)
def main():
    parser = _CreateOptionParser()
    options, _args = parser.parse_args()  # pylint: disable=unused-variable
    if options.trace_cc:
        parser.error("""--trace-cc is deprecated.

For basic jank busting uses, use  --trace-frame-viewer
For detailed study of ubercompositor, pass --trace-ubercompositor.

When in doubt, just try out --trace-frame-viewer.
""")

    if options.verbose:
        logging.getLogger().setLevel(logging.DEBUG)

    device = device_utils.DeviceUtils.HealthyDevices(
        device_arg=options.device_serial_number)[0]
    package_info = profiler.GetSupportedBrowsers()[options.browser]

    options.device = device
    options.package_info = package_info

    # Add options that are present in Systrace but not in profile_chrome (since
    # they both use the same tracing controller).
    # TODO(washingtonp): Once Systrace uses all of the profile_chrome agents,
    # manually setting these options will no longer be necessary and should be
    # removed.
    options.list_categories = None
    options.link_assets = None
    options.asset_dir = None
    options.timeout = None
    options.collection_timeout = None
    options.target = None

    if options.chrome_categories in ['list', 'help']:
        ui.PrintMessage('Collecting record categories list...', eol='')
        record_categories = []
        disabled_by_default_categories = []
        record_categories, disabled_by_default_categories = \
            chrome_tracing_agent.ChromeTracingAgent.GetCategories(
                device, package_info)

        ui.PrintMessage('done')
        ui.PrintMessage('Record Categories:')
        ui.PrintMessage('\n'.join('\t%s' % item \
            for item in sorted(record_categories)))

        ui.PrintMessage('\nDisabled by Default Categories:')
        ui.PrintMessage('\n'.join('\t%s' % item \
            for item in sorted(disabled_by_default_categories)))

        return 0

    if options.atrace_categories in ['list', 'help']:
        ui.PrintMessage('\n'.join(
            atrace_tracing_agent.AtraceAgent.GetCategories(device)))
        return 0

    if (perf_tracing_agent.PerfProfilerAgent.IsSupported()
            and options.perf_categories in ['list', 'help']):
        ui.PrintMessage('\n'.join(
            perf_tracing_agent.PerfProfilerAgent.GetCategories(device)))
        return 0

    if not options.trace_time and not options.continuous:
        ui.PrintMessage(
            'Time interval or continuous tracing should be specified.')
        return 1

    if options.chrome_categories and 'webview' in options.atrace_categories:
        logging.warning('Using the "webview" category in atrace together with '
                        'Chrome tracing results in duplicate trace events.')

    if options.output_file:
        options.output_file = os.path.expanduser(options.output_file)
    result = profiler.CaptureProfile(
        options,
        options.trace_time if not options.continuous else 0,
        _PROFILE_CHROME_AGENT_MODULES,
        output=options.output_file,
        compress=options.compress,
        write_json=options.write_json)
    if options.view:
        if sys.platform == 'darwin':
            os.system('/usr/bin/open %s' % os.path.abspath(result))
        else:
            webbrowser.open(result)
Beispiel #12
0
def main():
    parser = _CreateOptionParser()
    options, _args = parser.parse_args()
    if options.trace_cc:
        parser.parse_error("""--trace-cc is deprecated.

For basic jank busting uses, use  --trace-frame-viewer
For detailed study of ubercompositor, pass --trace-ubercompositor.

When in doubt, just try out --trace-frame-viewer.
""")

    if options.verbose:
        logging.getLogger().setLevel(logging.DEBUG)

    devices = android_commands.GetAttachedDevices()
    if len(devices) != 1:
        parser.error('Exactly 1 device must be attached.')
    device = device_utils.DeviceUtils(devices[0])
    package_info = profiler.GetSupportedBrowsers()[options.browser]

    if options.chrome_categories in ['list', 'help']:
        ui.PrintMessage('Collecting record categories list...', eol='')
        record_categories = []
        disabled_by_default_categories = []
        record_categories, disabled_by_default_categories = \
            chrome_controller.ChromeTracingController.GetCategories(
                device, package_info)

        ui.PrintMessage('done')
        ui.PrintMessage('Record Categories:')
        ui.PrintMessage('\n'.join('\t%s' % item \
            for item in sorted(record_categories)))

        ui.PrintMessage('\nDisabled by Default Categories:')
        ui.PrintMessage('\n'.join('\t%s' % item \
            for item in sorted(disabled_by_default_categories)))

        return 0

    if options.systrace_categories in ['list', 'help']:
        ui.PrintMessage('\n'.join(
            systrace_controller.SystraceController.GetCategories(device)))
        return 0

    if (perf_controller.PerfProfilerController.IsSupported()
            and options.perf_categories in ['list', 'help']):
        ui.PrintMessage('\n'.join(
            perf_controller.PerfProfilerController.GetCategories(device)))
        return 0

    if not options.time and not options.continuous:
        ui.PrintMessage(
            'Time interval or continuous tracing should be specified.')
        return 1

    chrome_categories = _ComputeChromeCategories(options)
    systrace_categories = _ComputeSystraceCategories(options)
    perf_categories = _ComputePerfCategories(options)

    if chrome_categories and 'webview' in systrace_categories:
        logging.warning(
            'Using the "webview" category in systrace together with '
            'Chrome tracing results in duplicate trace events.')

    enabled_controllers = []
    if chrome_categories:
        enabled_controllers.append(
            chrome_controller.ChromeTracingController(device, package_info,
                                                      chrome_categories,
                                                      options.ring_buffer,
                                                      options.trace_memory))
    if systrace_categories:
        enabled_controllers.append(
            systrace_controller.SystraceController(device, systrace_categories,
                                                   options.ring_buffer))

    if perf_categories:
        enabled_controllers.append(
            perf_controller.PerfProfilerController(device, perf_categories))

    if not enabled_controllers:
        ui.PrintMessage('No trace categories enabled.')
        return 1

    if options.output:
        options.output = os.path.expanduser(options.output)
    result = profiler.CaptureProfile(
        enabled_controllers,
        options.time if not options.continuous else 0,
        output=options.output,
        compress=options.compress,
        write_json=options.json)
    if options.view_canary:
        if sys.platform == 'darwin':
            os.system((
                '/usr/bin/open -a /Applications/Google\ Chrome\ Canary.app %s '
                '--args --enable-impl-side-painting --enable-skia-benchmarking '
                '--allow-webui-compositing') % os.path.abspath(result))
        else:
            _PrintMessage(
                'No Chrome Canary on this platform to open trace in, try a different view method'
            )
    elif options.view:
        if sys.platform == 'darwin':
            os.system('/usr/bin/open %s' % os.path.abspath(result))
        else:
            webbrowser.open(result)
    elif options.run_tev and result:
        os.system('trace-event-viewer %s' % result)
Beispiel #13
0
    elif len(devices) >= 2:
      raise RuntimeError('Multiple devices connected, serial number required')
    options.device_serial_number = devices[0]
  presentation.device = device_utils.DeviceUtils.HealthyDevices(device_arg=
      options.device_serial_number)[0]
  package_info = util.get_supported_browsers()[options.browser]

  options.presentation.device = presentation.device
  options.package_info = package_info

  # Include Chrome categories by default in profile_chrome.
  if not options.chrome_categories:
    options.chrome_categories = chrome_tracing_agent.DEFAULT_CHROME_CATEGORIES

  if options.chrome_categories in ['list', 'help']:
    ui.PrintMessage('Collecting record categories list...', eol='')
    record_categories = []
    disabled_by_default_categories = []
    record_categories, disabled_by_default_categories = \
        chrome_tracing_agent.ChromeTracingAgent.GetCategories(
            presentation.device, package_info)

    ui.PrintMessage('done')
    ui.PrintMessage('Record Categories:')
    ui.PrintMessage('\n'.join('\t%s' % item \
        for item in sorted(record_categories)))

    ui.PrintMessage('\nDisabled by Default Categories:')
    ui.PrintMessage('\n'.join('\t%s' % item \
        for item in sorted(disabled_by_default_categories)))