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
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