Beispiel #1
0
def print_profile_numbers(profile_numbers, profile_types, line_ending='\n'):
    """Helper function for printing the numbers of profile to output.

    :param dict profile_numbers: dictionary of number of profiles grouped by type
    :param str profile_types: type of the profiles (tracked, untracked, etc.)
    :param str line_ending: ending of the print (for different outputs of log and status)
    """
    if profile_numbers['all']:
        print("{0[all]} {1} profiles (".format(profile_numbers, profile_types),
              end='')
        first_printed = False
        for profile_type in SUPPORTED_PROFILE_TYPES:
            if not profile_numbers[profile_type]:
                continue
            print(', ' if first_printed else '', end='')
            first_printed = True
            type_colour = PROFILE_TYPE_COLOURS[profile_type]
            cprint(
                "{0} {1}".format(profile_numbers[profile_type], profile_type),
                type_colour)
        print(')', end=line_ending)
    else:
        cprintln('(no {} profiles)'.format(profile_types),
                 TEXT_WARN_COLOUR,
                 attrs=TEXT_ATTRS)
Beispiel #2
0
def collect(**kwargs):
    """ Runs the created SystemTap script and the profiled command

    The output dictionary is updated with:
     - output: path to the collector output file

    :param dict kwargs: dictionary containing the configuration settings for the collector
    :returns: (int as a status code, nonzero values for errors,
              string as a status message, mainly for error states,
              dict of kwargs and new values)
    """
    log.cprintln(
        'Running the collector, progress output stored in collect_log_{0}.txt\n'
        'This may take a while... '.format(kwargs['timestamp']), 'white')
    try:
        # Call the system tap
        code, kwargs['output_path'] = systemtap.systemtap_collect(**kwargs)
        result = _COLLECTOR_STATUS[code]
        if code != systemtap.Status.OK:
            log.failed()
            # Add specific return for STAP error which includes the precise log file path
            if code == systemtap.Status.STAP:
                return result[0], result[1].format(
                    kwargs['log_path']), dict(kwargs)
        return result[0], result[1], dict(kwargs)

    except (OSError, CalledProcessError) as exception:
        log.failed()
        return CollectStatus.ERROR, str(exception), dict(kwargs)
Beispiel #3
0
def systemtap_collect(script_path, log_path, output_path, cmd, args, **kwargs):
    """Collects performance data using the system tap wrapper, assembled script and
    external command. This function serves as a interface to the system tap collector.

    :param str script_path: path to the assembled system tap script file
    :param str log_path: path to the collection log file
    :param str output_path: path to the collection output file
    :param str cmd: the external command that contains / invokes the profiled executable
    :param list args: the arguments supplied to the external command
    :param kwargs: additional collector configuration
    :return tuple: containing the collection status, path to the output file of the collector
    """
    # Perform the cleanup
    if kwargs['cleanup']:
        _stap_cleanup()

    # Create the output and log file for collection
    with open(log_path, 'w') as logfile:
        # Start the SystemTap process
        log.cprint('Starting the SystemTap process... ', 'white')
        stap_pgid = set()
        try:
            stap_runner, code = start_systemtap_in_background(
                script_path, output_path, logfile, **kwargs)
            if code != Status.OK:
                return code, None
            stap_pgid.add(os.getpgid(stap_runner.pid))
            log.done()

            # Run the command that is supposed to be profiled
            log.cprint(
                'SystemTap up and running, execute the profiling target... ',
                'white')
            run_profiled_command(cmd, args, **kwargs)
            log.done()

            # Terminate SystemTap process after the file was fully written
            log.cprint(
                'Data collection complete, terminating the SystemTap process... ',
                'white')
            # _wait_for_fully_written(output_path)
            _wait_for_fully_written(output_path)
            kill_systemtap_in_background(stap_pgid)
            log.done()
            return Status.OK, output_path
        # Timeout was reached, inform the user but continue normally
        except exceptions.HardTimeoutException as e:
            kill_systemtap_in_background(stap_pgid)
            log.cprintln('', 'white')
            log.warn(e.msg)
            log.warn('The profile creation might fail or be inaccurate.')
            return Status.OK, output_path
        # Critical error during profiling or collection interrupted
        # make sure we terminate the collector and remove module
        except (Exception, KeyboardInterrupt):
            if not stap_pgid:
                stap_pgid = None
            # Clean only our mess
            _stap_cleanup(stap_pgid, output_path)
            raise
Beispiel #4
0
def log(pcs, minor_version, short=False, **_):
    """Prints the log of the performance control system

    Either prints the short or longer version. In short version, only header and short
    list according to the formatting string from stored in the configuration. Prints
    the number of profiles associated with each of the minor version and some basic
    information about minor versions, like e.g. description, hash, etc.

    Arguments:
        pcs(PCS): object with performance control system wrapper
        minor_version(str): representation of the head version
        short(bool): true if the log should be in short format
    """
    perun_log.msg_to_stdout("Running inner wrapper of the 'perun log '", 2)

    # Print header for --short-minors
    if short:
        print_short_minor_info_header()

    # Walk the minor versions and print them
    for minor in vcs.walk_minor_versions(pcs.vcs_type, pcs.vcs_path,
                                         minor_version):
        if short:
            print_short_minor_version_info(pcs, minor)
        else:
            cprintln("Minor Version {}".format(minor.checksum),
                     TEXT_EMPH_COLOUR,
                     attrs=TEXT_ATTRS)
            base_dir = pcs.get_object_directory()
            tracked_profiles = store.get_profile_number_for_minor(
                base_dir, minor.checksum)
            print_profile_numbers(tracked_profiles, 'tracked')
            print_minor_version_info(minor, indent=1)
Beispiel #5
0
def print_profile_info_list(profile_list,
                            max_lengths,
                            short,
                            list_type='tracked'):
    """Prints list of profiles and counts per type of tracked/untracked profiles.

    Prints the list of profiles, trims the sizes of each information according to the
    computed maximal lengths If the output is short, the list itself is not printed,
    just the information about counts. Tracked and untracked differs in colours.

    :param list profile_list: list of profiles of ProfileInfo objects
    :param dict max_lengths: dictionary with maximal sizes for the output of profiles
    :param bool short: true if the output should be short
    :param str list_type: type of the profile list (either untracked or tracked)
    """
    # Sort the profiles w.r.t time of creation
    profile.sort_profiles(profile_list)

    # Print with padding
    profile_output_colour = 'white' if list_type == 'tracked' else 'red'
    index_id_char = 'i' if list_type == 'tracked' else 'p'
    ending = ':\n\n' if not short else "\n"

    profile_numbers = calculate_profile_numbers_per_type(profile_list)
    print_profile_numbers(profile_numbers, list_type, ending)

    # Skip empty profile list
    profile_list_len = len(profile_list)
    profile_list_width = len(str(profile_list_len))
    if not profile_list_len or short:
        return

    # Load formating string for profile
    profile_info_fmt = perun_config.lookup_key_recursively('format.status')
    fmt_tokens, _ = FMT_SCANNER.scan(profile_info_fmt)

    # Compute header length
    header_len = profile_list_width + 3
    for (token_type, token) in fmt_tokens:
        if token_type == 'fmt_string':
            attr_type, limit, _ = FMT_REGEX.match(token).groups()
            limit = adjust_limit(limit, attr_type, max_lengths,
                                 (2 if attr_type == 'type' else 0))
            header_len += limit
        else:
            header_len += len(token)

    cprintln("\u2550" * header_len + "\u25A3", profile_output_colour)
    # Print header (2 is padding for id)
    print(" ", end='')
    cprint("id".center(profile_list_width + 2, ' '), profile_output_colour)
    print(" ", end='')
    for (token_type, token) in fmt_tokens:
        if token_type == 'fmt_string':
            attr_type, limit, _ = FMT_REGEX.match(token).groups()
            limit = adjust_limit(limit, attr_type, max_lengths,
                                 (2 if attr_type == 'type' else 0))
            token_string = attr_type.center(limit, ' ')
            cprint(token_string, profile_output_colour, [])
        else:
            # Print the rest (non token stuff)
            cprint(token, profile_output_colour)
    print("")
    cprintln("\u2550" * header_len + "\u25A3", profile_output_colour)
    # Print profiles
    for profile_no, profile_info in enumerate(profile_list):
        print(" ", end='')
        cprint(
            "{}@{}".format(profile_no,
                           index_id_char).rjust(profile_list_width + 2, ' '),
            profile_output_colour)
        print(" ", end='')
        for (token_type, token) in fmt_tokens:
            if token_type == 'fmt_string':
                attr_type, limit, fill = FMT_REGEX.match(token).groups()
                limit = adjust_limit(limit, attr_type, max_lengths)
                print_formating_token(profile_info_fmt,
                                      profile_info,
                                      attr_type,
                                      limit,
                                      default_color=profile_output_colour,
                                      value_fill=fill or ' ')
            else:
                cprint(token, profile_output_colour)
        print("")
        if profile_no % 5 == 0 or profile_no == profile_list_len - 1:
            cprintln("\u2550" * header_len + "\u25A3", profile_output_colour)
Beispiel #6
0
def log(minor_version, short=False, **_):
    """Prints the log of the performance control system

    Either prints the short or longer version. In short version, only header and short
    list according to the formatting string from stored in the configuration. Prints
    the number of profiles associated with each of the minor version and some basic
    information about minor versions, like e.g. description, hash, etc.

    :param str minor_version: representation of the head version
    :param bool short: true if the log should be in short format
    """
    perun_log.msg_to_stdout("Running inner wrapper of the 'perun log '", 2)

    # Print header for --short-minors
    if short:
        minor_versions = list(vcs.walk_minor_versions(minor_version))
        # Reduce the descriptions of minor version to one liners
        for mv_no, minor_version in enumerate(minor_versions):
            minor_versions[mv_no] = minor_version._replace(
                desc=minor_version.desc.split("\n")[0])
        minor_version_maxima = calculate_maximal_lengths_for_object_list(
            minor_versions, MinorVersion._fields)

        # Update manually the maxima for the printed supported profile types, each requires two
        # characters and 9 stands for " profiles" string

        def minor_stat_retriever(minor_v):
            """Helper function for picking stats of the given minor version

            :param MinorVersion minor_v: minor version for which we are retrieving the stats
            :return: dictionary with stats for minor version
            """
            return store.get_profile_number_for_minor(
                pcs.get_object_directory(), minor_v.checksum)

        def deg_count_retriever(minor_v):
            """Helper function for picking stats of the degradation strings of form ++--

            :param MinorVersion minor_v: minor version for which we are retrieving the stats
            :return: dictionary with stats for minor version
            """
            counts = perun_log.count_degradations_per_group(
                store.load_degradation_list_for(pcs.get_object_directory(),
                                                minor_v.checksum))
            return {
                'changes':
                counts.get('Optimization', 0) * '+' +
                counts.get('Degradation', 0) * '-'
            }

        minor_version_maxima.update(
            calculate_maximal_lengths_for_stats(minor_versions,
                                                minor_stat_retriever))
        minor_version_maxima.update(
            calculate_maximal_lengths_for_stats(minor_versions,
                                                deg_count_retriever,
                                                " changes "))
        print_short_minor_version_info_list(minor_versions,
                                            minor_version_maxima)
    else:
        # Walk the minor versions and print them
        for minor in vcs.walk_minor_versions(minor_version):
            cprintln("Minor Version {}".format(minor.checksum),
                     TEXT_EMPH_COLOUR,
                     attrs=TEXT_ATTRS)
            base_dir = pcs.get_object_directory()
            tracked_profiles = store.get_profile_number_for_minor(
                base_dir, minor.checksum)
            print_profile_numbers(tracked_profiles, 'tracked')
            print_minor_version_info(minor, indent=1)