Source code for mcgdb.model.task.environment.openmp.interaction.sequence

from __future__ import print_function

import os, sys, inspect
import logging; log = logging.getLogger(__name__)

PY3 = sys.version_info >= (3, 0)

import gdb

from mcgdb.toolbox import my_gdb
from mcgdb.toolbox import aspect

on_update = set()

@my_gdb.internal
[docs]def initialize(): aspect.register("sequence", sequence_aspects, pre_aspect)
@my_gdb.internal
[docs]def activate(): seq = cmd_omp_sequence() from . import graph param_graph_auto() param_graph_auto.graph_cmd = seq
[docs]class param_graph_auto (gdb.Parameter): auto_graph_enabled = False graph_cmd = None @staticmethod
[docs] def on_stop(evt=None): assert param_graph_auto.graph_cmd is not None if param_graph_auto.graph_cmd.has_changed(): param_graph_auto.graph_cmd.invoke("--no-gen --async --no-write", from_tty=False)
def __init__ (self): gdb.Parameter.__init__(self, "omp-auto-sequence", gdb.COMMAND_OBSCURE, gdb.PARAM_BOOLEAN) self.value = False old_prompt = gdb.prompt_hook def auto_graph_prompt(prompt): if param_graph_auto.auto_graph_enabled: param_graph_auto.on_stop() if old_prompt: prompt = old_prompt(prompt) return prompt gdb.prompt_hook = auto_graph_prompt
[docs] def get_set_string(self): if not param_graph_auto.auto_graph_enabled and self.value: # enable gdb.events.stop.connect(param_graph_auto.on_stop) assert param_graph_auto.graph_cmd is not None #param_graph_auto.graph_cmd.invoke("--open", from_tty=False) msg = "OpenMP auto graph enabled" elif param_graph_auto.auto_graph_enabled and not self.value: # disable gdb.events.stop.disconnect(param_graph_auto.on_stop) msg = "OpenMP auto graph disabled" else: # nothing to do, value unchanged msg = "Nothing changed, auto graph is {}".format(self.value) param_graph_auto.auto_graph_enabled = self.value return msg
[docs] def get_show_string (self, svalue): return "OpenMP auto graph is currently {}".format(svalue)
top_block = None
[docs]class cmd_omp_sequence(gdb.Command): @classmethod
[docs] def has_changed(clazz): return \ clazz.changed \ or clazz.sched_locked != gdb.parameter("scheduler-locking") \ or clazz.thread is not gdb.selected_thread()
self = None changed = True thread_num = None sched_locked = None def __init__ (self): gdb.Command.__init__(self, "omp sequence", gdb.COMMAND_OBSCURE) self.count = 0
[docs] def invoke (self, args, from_tty): self.dont_repeat() only_current = "--all" not in args do_print = "--print" in args or "--show" in args do_open = "--open" in args do_not_gen = "--no-gen" in args do_not_write = "--no-write" in args do_sync = "--sync" in args do_async = "--async" in args do_gen_png = True # for now if do_print: do_not_gen = True if do_open: do_sync = True if do_async: do_sync = True # for now Block.print_only_current = only_current seqdiag = str(top_block) if PY3 else \ u"{}".format(top_block).encode('UTF-8') Block.print_only_current = False cmd_omp_sequence.changed = False cmd_omp_sequence.sched_locked = gdb.parameter("scheduler-locking") cmd_omp_sequence.thread = gdb.selected_thread() if seqdiag == "None": seqdiag = """{\n 1;\n 1 -> 1 [here];\n 1 <- 1 [narrow]\n}""" if do_print: print(seqdiag) if not do_not_write: with open("run", "w") as f: print(seqdiag, file=f) for update_listener in on_update: update_listener(seqdiag) if not do_not_gen: svg_command = "seqdiag run -T svg" png_command = "convert run.svg run.png" if do_not_gen else "echo ''" sync = "" if do_sync else "&" os.system('sh -c "{} ; {}" {}'.format(svg_command, png_command, sync)) if do_open: os.system("eog run.png >/dev/null 2>/dev/null &")
[docs]def pre_aspect(help_tracking, self, args): cmd_omp_sequence.changed = True
[docs]class Edge: def __init__(self, from_node, to_node, backwards=False, **kwargs): self.from_node = from_node self.to_node = to_node self.forwards = not backwards self.attribs = kwargs self.zindex = 0 def __str__(self): edge = "{from} {dir} {to}".format(**{ "from": self.from_node.name, "to": self.to_node.name, "dir": "->" if self.forwards else "<-" }) if self.attribs: attr = ", ".join([name for name, value in self.attribs.items() \ if value is None] +\ [u'{}="{}"'.format(name, value) \ for name, value in self.attribs.items() \ if value is not None] ) attr = u" [{}]".format(attr) else: attr = "" return u"{}{}".format(edge, attr)
[docs]def elipse_str(string): MAX_LEN = 12 if len(string) > MAX_LEN: string = string[:MAX_LEN - 1] string += u"\u2026" # single char '...' return string
[docs]class Node: first = None all_nodes = set() def __init__(self, name, worker): Node.all_nodes.add(self) self.name = name self.block = None self.worker = worker if Node.first is None: Node.first = self
[docs] def msg(self, **kwargs): self.block.edges.append(Edge(self, self, msg=None, **kwargs))
def __str__(self): return "{}".format(self.name)
[docs]class Block: print_only_current = False def __init__(self, parent, _type, label=None, zindex=0): self.type = _type self.nodes = set() self.working = set() self.label = label self.edges = [] self.parent = parent self.zindex = zindex if parent is not None: parent.edges.append(self) @property def zordered_edges(self): return sorted(self.edges, key=lambda e: e.zindex, reverse=True)
[docs] def add_node(self, node, **kwargs): self.nodes.add(node) node.block = self
[docs] def flow(self, from_node, to_node, **kwargs): self.edges.append(Edge(from_node, to_node, **kwargs)) self.working.add(from_node) self.working.add(to_node)
[docs] def enter(self, node, **kwargs): self.edges.append(Edge(node, node, **kwargs)) self.working.add(node)
[docs] def exit(self, node, no_insert=False, **kwargs): edge = Edge(node, node, backwards=True, **kwargs) if not no_insert: self.edges.append(edge) try: self.working.remove(node) except: log.error("Node {} doesn't belong to {}/{}".format(node, self, self.working)) return edge
[docs] def leave(self, node): node.block = self.parent
[docs] def finish(self): for node in self.nodes: if node.block is self: node.block.leave(node)
# ret: <has_worker>, <child_has_worker>
[docs] def has_worker(self, recursive=False): # has_worker ? for node in self.nodes: if node.block is self: return True, None # don't dive, so no if not recursive: return False, None # no worker, child_has_worker ? for block in self.edges: if not isinstance(block, Block): continue if any(block.has_worker(recursive=True)): return False, True return False, False
def __str__(self): def want_edge(edge): if Block.print_only_current: if not isinstance(edge, Block): # only blocks should be hidden return True block = edge has_worker, child_has_worker = block.has_worker(recursive=True) return has_worker or child_has_worker return True type_ = "{} ".format(self.type) if self.type is not None else "" if self.type and self.label: label = '"{}"'.format(self.label) if " " in self.label else self.label type_ += '{} '.format(label) node_list = self.nodes if self.parent is not None \ else sorted(Node.all_nodes, key=lambda n : int(n.name)) nodes = "; ".join(map(str, node_list))+";" edges = u";\n ".join(u"{}".format(edge).replace("\n", "\n ") \ for edge in self.zordered_edges if want_edge(edge)) # check if we have to set the "here" attribute for node in self.nodes: if node.block is not self: continue edges += u"\n {};".format(Edge(node, node, here=None)) if node in self.working and not self.parent is None: edges += "\n {};".format(self.exit(node, no_insert=True, narrow=None)) # root block sets some more attributes ... if self.parent is None: # implicite return from main if True or node not in self.working: edges += "\n "+str(Edge(Node.first, Node.first, backwards=True, narrow=None)) # put the active worker(s) in a group to highlight it cur_worker = Node.first.worker.__class__.get_current_worker() def is_active(node): return node.worker is cur_worker active = [str(node) for node in Node.all_nodes if is_active(node)] if active: attribs = "" if gdb.parameter("scheduler-locking") == "on": attribs += "color=lightcoral;" if attribs: attribs = "\n" + attribs nodes += """\n group {{{} {} }}""".format(attribs, "; ".join(active)+";").replace("\n", "\n ") return u"""{}{{ {} {} }}""".format(type_, nodes, edges)
[docs]class Barrier(Block): def __str__(self): has_body = False barrier = "=== <{}> ===".format(self.type) edges = u"" if self.edges: edges += u"\n".join(u"{}".format(edge) for edge in self.zordered_edges) has_body = True for node in self.nodes: if node.block is self: edges += u"\n{};".format(Edge(node, node, here=None)) has_body = True if has_body: stop = "=== </{}> ===".format(self.type) barrier = u"\n".join((barrier, edges, stop)) return barrier
[docs]def sequence_aspects(Tracks): from .. import representation @Tracks(representation.Worker) class WorkerTracker: def __init__(this): this.node = Node(str(this.self.number), this.self) if this.self.number == 1: global top_block top_block = Block(None, None) top_block.add_node(this.node) top_block.enter(this.node, leftnote="main") @Tracks(representation.ParallelJob) class ParallelJobTracker: def __init__(this): this.args.num_workers, this.args.parent_worker = this.meth_args this.block = Block(top_block, "parallel{}".format(" for" if this.self.is_for else "")) def start_working(this): node = this.self_to_this[this.args.worker].node this.block.add_node(node) def stop_working(this): pass def completed(this): this.block.finish() @Tracks(representation.SingleJob) class SingleTracker: def __init__(this, before=False): this.args.worker, this.args.parallel_job = this.meth_args if not before: this.barrier = this.self_to_this[this.args.parallel_job.barrier].barrier return node = this.self_to_this[this.args.worker].node this.parallel_block = this.self_to_this[this.args.parallel_job].block this.block = Block(this.parallel_block, "single") def enter(this): node = this.self_to_this[this.args.worker].node this.block.add_node(node) if this.args.inside: this.block.enter(node, label="enter") def finished(this): node = this.self_to_this[this.self.visitor].node this.block.exit(node, label="exit") def completed(this): this.block.finish() @Tracks(representation.TaskJob) class TaskJobTracker: def __init__(this): this.args.worker, = this.meth_args if not this.self.masterTask and this.args.worker: node = this.self_to_this[this.args.worker].node node.msg(label="Task {}".format(this.self.number)) def start(this): this.node = this.self_to_this[this.args.worker].node # 'task' type is special, there can be several on the same line, # but they're not handled correctly at the end of a block this.block = Block(this.node.block if this.node.block is not None else top_block, *(("single", "master") if this.self.masterTask \ else ("task", None))) this.block.add_node(this.node) #for dep in this.self.in_dependencies: # this.node.msg(label="With dependency #{}".format(dep.number)) this.block.enter(this.node, label="Task {}".format(this.self.number)) def finish(this): this.block.exit(this.node) #for dep in this.self.out_dependencies: # this.node.msg(label="Dependency #{} ready".format(dep.number)) this.block.leave(this.node) @Tracks(representation.CriticalJob) class CriticalJobTracker: def __init__(this): this.args.worker, this.args.parallel_job = this.meth_args node = this.self_to_this[this.args.worker].node this.parallel_block = this.self_to_this[this.args.parallel_job].block this.block = Block(this.parallel_block, "critical") this.inside = None def try_enter(this): node = this.self_to_this[this.args.worker].node this.block.add_node(node) def entered(this): prev_inside = this.inside this.inside = this.self_to_this[this.args.worker].node if prev_inside: this.block.flow(prev_inside, this.inside) else: this.block.enter(this.inside, label="enter") def left(this): pass def completed(this): this.block.exit(this.inside, label="finish") this.block.finish() @Tracks(representation.Barrier) class BarrierTracker: def __init__(this): this.args.parallel_job, this.args.worker = this.meth_args if "single" in this.args: parent_zone = this.args.single zindex = -1 else: parent_zone = this.args.parallel_job zindex = 0 this.parent_block = this.self_to_this[parent_zone].block this.barrier = Barrier(this.parent_block, "Barrier {}".format(this.self.number), zindex=zindex) def reach_barrier(this): node = this.self_to_this[this.args.worker].node this.barrier.add_node(node) def leave_barrier(this): node = this.self_to_this[this.args.worker].node this.barrier.leave(node) if this.self.single: single = this.self_to_this[this.self.single].block single.leave(node) @Tracks(representation.SectionJob) class SectionJobTracker: def __init__(this): this.args.parallel_zone, this.args.worker, this.args.count = \ this.meth_args parallel_block = this.self_to_this[this.args.parallel_zone].block this.block = Block(parallel_block, "section") this.working = set() node = this.self_to_this[this.args.worker].node this.block.add_node(node) def work_on_section(this): node = this.self_to_this[this.args.worker].node this.block.add_node(node) if node in this.working: this.working.remove(node) this.block.exit(node) if this.args.section_id != 0: this.block.enter(node, label="Section #{}".format(this.args.section_id)) this.working.add(node) def completed(this): this.block.finish()