Exemple #1
0
    def parse_units(self, node, resource):
        """Recursively parse the unit node until all of them are found"""
        # Map of entities for a ressource
        entities_messages = []

        if not node.findall('./unit'):
            return []

        for unit in node.findall('./unit'):

            ename = unit.attrib.get('name')

            ekind = unit.attrib.get('kind')
            if self.firstunit:
                ekind = "compilation unit"
                self.firstunit = False
            else:
                if ekind.startswith('procedure'):
                    ekind = ekind.replace("procedure", "action")
                elif ekind.startswith('function'):
                    ekind = ekind.replace("function", "action")

            eline = unit.attrib.get('line')
            ecol = unit.attrib.get('col')

            # A resource can have multiple entities with the same name
            entity = GNAThub.Entity(ename, ekind, int(eline), int(ecol),
                                    int(ecol), resource)

            entities_messages.append([entity, self.parse_metrics(unit, True)])
            entities_messages += self.parse_units(unit, resource)
        return entities_messages
Exemple #2
0
    def report(self):
        """Parse GNATstack output file report.

        Returns according to the success of the analysis:

            * ``GNAThub.EXEC_SUCCESS``: on successful execution and analysis
            * ``GNAThub.EXEC_FAILURE``: on any error
        """
        # Clear existing references only if not incremental run
        if not GNAThub.incremental():
            self.log.info('clear existing results if any')
            GNAThub.Tool.clear_references(self.name)

        self.info('analyse report')

        self.tool = GNAThub.Tool(self.name)
        if not os.path.exists(self.output):
            self.error('no report found')
            return GNAThub.EXEC_FAILURE
        self.log.debug('parse XML report: %s', self.output)

        # List of resource messages suitable for tool level bulk insertion
        resources_messages = []
        # Map of list of messages by entities
        entities_messages_map = {}
        # List of entity messages suitable for tool level bulk insertion
        entities_messages = []
        # List of indirect call location
        indirect_loc_list = []

        try:
            tree = ElementTree.parse(self.output)
            global_node = tree.find('./global')

            # Retrieve the metrics and the map of subprogram by id
            subprograms = tree.find('./subprogramset').findall('./subprogram')
            if subprograms:
                unknown_global = GNAThub.Rule("Unknown Global Stack Usage",
                                              "Unknown Global Stack Usage",
                                              GNAThub.METRIC_KIND, self.tool)
                static_global = GNAThub.Rule("Static Global Stack Usage",
                                             "Static Global Stack Usage",
                                             GNAThub.METRIC_KIND, self.tool)
                unknown_local = GNAThub.Rule("Unknown Local Stack Usage",
                                             "Unknown Local Stack Usage",
                                             GNAThub.METRIC_KIND, self.tool)
                static_local = GNAThub.Rule("Static Local Stack Usage",
                                            "Static Local Stack Usage",
                                            GNAThub.METRIC_KIND, self.tool)
            for node in subprograms:
                subprogram_id = node.attrib.get('id')
                locations = node.find('./locationset').findall('./location')
                global_usage = node.find('./globalstackusage')
                local_usage = node.find('./localstackusage')
                name = node.attrib.get('prefixname')
                if name == "indirect call":
                    # The columns are only defined here so save them for later
                    line = locations[0].attrib.get('line')
                    column = locations[0].attrib.get('column')
                    indirect_loc_list.append([line, column])
                    continue
                else:
                    name = self.pp_name(name)

                if not locations:
                    if subprogram_id not in self.subprograms_without_location:
                        self.subprograms_without_location[subprogram_id] = name

                for loc in locations:
                    file = loc.attrib.get('file')
                    line = loc.attrib.get('line')
                    column = loc.attrib.get('column')
                    if file in self.resources:
                        resource = self.resources[file]
                    else:
                        resource = GNAThub.Resource(file, GNAThub.FILE_KIND)
                        self.resources[file] = resource

                    # entities default value for kind is set to "procedure"
                    entity = GNAThub.Entity(name, "action", int(line),
                                            int(column), int(column), resource)
                    # Only link the id to the first location of the entity
                    if subprogram_id not in self.subprograms:
                        self.subprograms[subprogram_id] = entity
                    else:
                        continue

                    size = global_usage.attrib.get('size')
                    if global_usage.attrib.get('qualifier') == "UNKNOWN":
                        metric = unknown_global
                    else:
                        metric = static_global
                    global_metric = GNAThub.Message(metric,
                                                    size,
                                                    ranking=GNATstack.RANKING)

                    size = local_usage.attrib.get('size')
                    if local_usage.attrib.get('qualifier') == "UNKNOWN":
                        metric = unknown_local
                    else:
                        metric = static_local
                    local_metric = GNAThub.Message(metric,
                                                   size,
                                                   ranking=GNATstack.RANKING)

                    entities_messages_map[subprogram_id] = ([[
                        global_metric, 0, 1, 1
                    ], [local_metric, 0, 1, 1]])

            # Analyse the indirect calls
            indirects = global_node.find('./indirectset').findall('./indirect')
            if indirects:
                indirect_rule = GNAThub.Rule("Indirect Call", "Indirect Call",
                                             GNAThub.RULE_KIND, self.tool)
            for node in indirects:
                indirect_id = node.attrib.get('id')
                if indirect_id not in self.subprograms:
                    continue

                set = node.find('./indirectcallset').findall('./indirectcall')
                for call in set:
                    line = call.find('./line').find('./value').text
                    # Go through the list of saved locations and use the
                    # line to retrieve a corresponding column
                    column = 1
                    pos = -1
                    for ix in range(len(indirect_loc_list)):
                        if indirect_loc_list[ix][0] == line:
                            pos = ix
                            column = indirect_loc_list[pos][1]
                            continue
                    if pos != -1:
                        indirect_loc_list.pop(pos)
                    message = GNAThub.Message(indirect_rule,
                                              self.pp_msg(
                                                  indirect_id,
                                                  "indirect call"),
                                              ranking=GNATstack.RANKING)
                    entities_messages_map[indirect_id].append(
                        [message, int(line),
                         int(column),
                         int(column)])

            # Analyse the external calls
            externals = global_node.find('./externalset').findall('./external')
            if externals:
                external_rule = GNAThub.Rule("External Call", "External Call",
                                             GNAThub.RULE_KIND, self.tool)
            for node in externals:
                subprogram_id = node.attrib.get('id')
                if subprogram_id not in self.subprograms:
                    continue
                message = GNAThub.Message(external_rule,
                                          self.pp_msg(subprogram_id,
                                                      "external call"),
                                          ranking=GNATstack.RANKING)
                entities_messages_map[subprogram_id].append([message, 0, 1, 1])

            # Analyse the potential cycle
            cycles = global_node.find('./cycleset').findall('./cycle')
            if cycles:
                cycle_rule = GNAThub.Rule("Potential Cycle", "Potential Cycle",
                                          GNAThub.RULE_KIND, self.tool)
            for node in cycles:
                cycle_subprograms = node.findall('./subprogram')
                cycle_list = []
                for sub in cycle_subprograms:
                    subprogram_id = sub.attrib.get('id')
                    cycle_list.append(self.subprograms[subprogram_id].name)
                subprogram_id = cycle_subprograms[0].attrib.get('id')
                cycle_list.append(self.subprograms[subprogram_id].name)
                message = GNAThub.Message(cycle_rule,
                                          "potential cycle detected:\n\t\t" +
                                          "\n\t\t".join(cycle_list),
                                          ranking=GNATstack.RANKING)
                entities_messages_map[subprogram_id].append([message, 0, 1, 1])

            # Analyse the unbounded frames
            unboundeds = (
                global_node.find('./unboundedset').findall('./unbounded'))
            if unboundeds:
                unbounded_rule = GNAThub.Rule("Unbounded Frame",
                                              "Unbounded Frame",
                                              GNAThub.RULE_KIND, self.tool)
            for node in unboundeds:
                subprogram_id = node.attrib.get('id')
                if subprogram_id in self.subprograms:
                    message = GNAThub.Message(unbounded_rule,
                                              self.pp_msg(
                                                  subprogram_id,
                                                  "unbounded frame"),
                                              ranking=GNATstack.RANKING)
                    entities_messages_map[subprogram_id].append(
                        [message, 0, 1, 1])

            # Analyse the entry points
            entries = tree.find('./entryset').findall('./entry')
            # There is always an entry, so create the rule anyway
            entry_rule = GNAThub.Rule("Entry point", "Entry point",
                                      GNAThub.RULE_KIND, self.tool)
            for node in entries:
                subprogram_id = node.attrib.get('id')

                if subprogram_id not in self.subprograms:
                    continue
                entity = self.subprograms[subprogram_id]
                local_stack = node.find('./localstackusage')
                size = local_stack.attrib.get('size')
                qualifier = local_stack.attrib.get('qualifier')

                if qualifier == "UNKNOWN":
                    text = "The estimated"
                else:
                    text = "The"
                text += (' call stack size for the entry point "%s" is %s' %
                         (entity.name, str(size)))

                callchain_list = []
                for sub in node.find('./callchain').findall('./subprogram'):
                    chain_id = sub.attrib.get('id')
                    if chain_id in self.subprograms:
                        callchain_list.append(self.subprograms[chain_id].name)
                    elif chain_id in self.subprograms_without_location:
                        callchain_list.append(
                            self.subprograms_without_location[chain_id])
                    else:
                        continue

                text += (" and the callchain is:\n\t\t%s" %
                         "\n\t\t".join(callchain_list))
                message = GNAThub.Message(entry_rule,
                                          text,
                                          ranking=GNATstack.RANKING)
                entities_messages_map[subprogram_id].append([message, 0, 1, 1])

            # Project message explaining the accuracy of the metrics
            accurate = global_node.find('./accurate')
            if accurate.find('./value').text == "FALSE":
                project = GNAThub.Resource(GNAThub.Project.name(),
                                           GNAThub.PROJECT_KIND)
                rule = GNAThub.Rule("Accuracy", "Accuracy", GNAThub.RULE_KIND,
                                    self.tool)
                text = ("worst case may not be accurate because of: " +
                        ("indirect calls/" if indirects else "") +
                        ("cycles/" if cycles else "") +
                        ("unbounded frames/" if unboundeds else "") +
                        ("external calls" if externals else ""))
                text = text[:-1] if text[-1] == '/' else text
                message = GNAThub.Message(rule,
                                          text,
                                          ranking=GNATstack.RANKING)
                resources_messages.append([project, [[message, 0, 1, 1]]])

            # Insert the messages and the metrics
            for key, value in entities_messages_map.items():
                entities_messages.append([self.subprograms[key], value])
            self.tool.add_messages(resources_messages, entities_messages)
        except ParseError as why:
            self.log.exception('failed to parse XML report')
            self.error('%s (%s:%s)' % (why, why.filename, why.lineno))
            return GNAThub.EXEC_FAILURE
        else:
            return GNAThub.EXEC_SUCCESS