From 3f918f55e5598f2b6afae4e8ddb0f9cbe2a29c34 Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@imag.fr>
Date: Tue, 27 Sep 2016 14:13:37 +0200
Subject: [PATCH] work on omp profiling

---
 model/__init__.py                             |  2 +-
 model/numa/__init__.py                        | 52 +++++++++----
 model/profiling/info/__init__.py              |  2 +-
 model/profiling/info/perf_standalone.py       |  2 -
 model/profiling/interaction/graph.py          | 74 +++++++++++++++++--
 model/profiling/interaction/summary.py        |  6 +-
 .../openmp/capture/iomp/kmpc_for_static.py    | 20 +++--
 .../environment/openmp/interaction/loop.py    | 14 ++--
 .../environment/openmp/interaction/profile.py |  6 +-
 9 files changed, 136 insertions(+), 42 deletions(-)

diff --git a/model/__init__.py b/model/__init__.py
index 5827848..5891cdd 100644
--- a/model/__init__.py
+++ b/model/__init__.py
@@ -1,2 +1,2 @@
-from . import gpu, task, profiling
+from . import gpu, task, profiling, numa
 
diff --git a/model/numa/__init__.py b/model/numa/__init__.py
index 45ac7d8..07a9fd1 100644
--- a/model/numa/__init__.py
+++ b/model/numa/__init__.py
@@ -7,13 +7,14 @@ import logging; log = logging.getLogger(__name__)
 log_user = logging.getLogger("mcgdb.log.user.numa")
 
 import os
+import subprocess
 
 try:
     import shutil
 except ImportError:
     shutil = None
 
-numa_loaded = False
+numa_loaded = None
 
 PAGEMAP_PATH = "/home/videau/"
 PAGEMAP = "pagemap"
@@ -22,14 +23,22 @@ pagemap_bin = None
 
 class cmd_numa_pagemap(gdb.Command):
     def __init__ (self):
-        gdb.Command.__init__ (self, "pagemap", gdb.COMMAND_NONE)
+        gdb.Command.__init__ (self, "numa pagemap", gdb.COMMAND_NONE)
         global pagemap_bin
-        pagemap_bin = PAGEMAP
-        
-        if (shutil and shutil.which(PAGEMAP) # > py3.3 
-            or "/{}".format(PAGEMAP) in os.system("which {}").format(PAGEMAP))):
-            # nothing to do, we found it
-        else:
+
+        has_pagepage = False
+        try:
+            has_pagepage = shutil.which(PAGEMAP) is not None  # > py3.3
+        except AttributeError: # py2: shutil doesn't have which
+            pass
+
+        if not has_pagepage:
+            try:
+                has_pagepage = "/{}".format(PAGEMAP) in subprocess.check_output("which {}".format(PAGEMAP), shell=True, stderr=subprocess.STDOUT)
+            except subprocess.CalledProcessError: # which returned false
+                pass
+                
+        if not has_pagepage:
             pagemap_path = os.path.join(PAGEMAP_PATH, PAGEMAP)
             
             if not os.path.isfile(pagemap_path):
@@ -39,9 +48,21 @@ class cmd_numa_pagemap(gdb.Command):
                 pagemap_bin = pagemap_path
                 
     def invoke (self, args, from_tty):
-        addr = gdb.parse_and_eval(args)
-        os.system("{} -n {} {}".format(pagemap_bin, gdb.selected_inferior().pid, addr))
+        try:
+            addr = gdb.parse_and_eval(args)
+        except gdb.error as e:
+            log_user.error("Cannot evaluate '{}': {}".format(args, e))
+            return
+        
+        cmd = "{} -n {} {}".format(pagemap_bin, gdb.selected_inferior().pid, addr)
+        res = subprocess.check_output(cmd, shell=True)
+        try:
+            pm_addr, _, node = res[:-1].split(" ")
 
+            log_user.info("Address 0x{} is located on node {}".format(pm_addr, node))
+        except ValueError: # couldnt split correctly
+            log_user.warn("Unexpected response from pagemap: '{}'".format(res[:-1]))
+            
 class cmd_numa_current_node(gdb.Command):
     def __init__ (self):
         gdb.Command.__init__ (self, "numa current_node", gdb.COMMAND_NONE)
@@ -49,12 +70,13 @@ class cmd_numa_current_node(gdb.Command):
     def invoke (self, args, from_tty):
         for symb in ("numa_node_of_cpu", "sched_getcpu"):
             if not gdb.lookup_symbol(symb):
-                log_user.error("Symbol '{}' not found, cannot get current node.".format(symb)
-                               return
+                log_user.error("Symbol '{}' not found, cannot get current node.".format(symb))
+                return
         
         with my_gdb.set_parameter("scheduler-locking", "on"):
-            gdb.execute("p numa_node_of_cpu(sched_getcpu())")
-
+            node = gdb.execute("p numa_node_of_cpu(sched_getcpu())", to_string=True)
+        node = int(node.partition("= ")[-1])
+        log_user.info("Current node is N{}".format(node))
 
 class cmd_numa(gdb.Command):
     def __init__(self, limited=False):
@@ -69,7 +91,7 @@ class cmd_numa(gdb.Command):
 
         on_activated()
         numa_loaded = True
-            
+        log_user.info("Numa module successfully loaded.")
         if args:
             gdb.execute("numa {}".format(args))
 
diff --git a/model/profiling/info/__init__.py b/model/profiling/info/__init__.py
index c8d8b6c..90beb36 100644
--- a/model/profiling/info/__init__.py
+++ b/model/profiling/info/__init__.py
@@ -6,7 +6,7 @@ log_user = logging.getLogger("mcgdb.log.user")
 from mcgdb.toolbox import my_gdb
 
 # will be added to global scope
-info_counters = "perf_standalone", #"proc", "hit", "counter", "perf", "gprof", "memleaks"
+info_counters = "perf_standalone", "omp" #"proc", "hit", "counter", "perf", "gprof", "memleaks"
 info_counter_classes = None # will be populated in initialize()
 disabled_counters = set()
 
diff --git a/model/profiling/info/perf_standalone.py b/model/profiling/info/perf_standalone.py
index b131a48..dba3ef9 100644
--- a/model/profiling/info/perf_standalone.py
+++ b/model/profiling/info/perf_standalone.py
@@ -41,7 +41,6 @@ class perf_info_standalone():
             import signal, time, os
             old = signal.signal(signal.SIGUSR1, handler)
 
-        log.error("start perf")
         command = ['perf', 'stat', '-x,',
                    '-e', counters.param_perf_counters.get()
                    ]
@@ -56,7 +55,6 @@ class perf_info_standalone():
         self.perf = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
         self.res = "running"
         assert self.perf.pid > 1
-        log.error("perf pid is {}".format(self.perf.pid))
 
         if not DISABLE_STARTUP_SYNC:
             cnt = MAX_WAIT
diff --git a/model/profiling/interaction/graph.py b/model/profiling/interaction/graph.py
index 17ed168..4d6de6b 100644
--- a/model/profiling/interaction/graph.py
+++ b/model/profiling/interaction/graph.py
@@ -49,10 +49,8 @@ def comparison(record, name):
     plotly.offline.plot(fig, filename='comparison.html')
 
 def sequential(record, name):
-    print('chart.add("{}", {})'.format(name, record))
-    print()
-    with open("data.gplt", "w") as out:
-        out.write("")
+    print('{} | {}'.format(name, " ".join(map(str, record))))
+
     return
     
     data = [plotly.graph_objs.Bar(
@@ -152,7 +150,7 @@ class cmd_profile_graph_plot (gdb.Command):
         
     def invoke (self, args, from_tty, show_data=False, show_keys=False):
         prof_id, _, key = args.partition(" ")
-        prof_type = "first"
+        prof_type = "first last"
         
         if prof_id.isdigit():
             prof_id = int(prof_id)
@@ -170,6 +168,10 @@ class cmd_profile_graph_plot (gdb.Command):
 
                 if prof_id != "all":
                     prof_id = int(prof_id)
+                    
+                if not key:
+                    raise Exception("no key ...")
+                
             except Exception:
                 log_user.error("Expected [first|soustractive|function] <profile id|all> <key> arguments.")
                 return
@@ -183,11 +185,14 @@ class cmd_profile_graph_plot (gdb.Command):
                 continue
 
             found = True
-
+            
+            if prof.running:
+                log_user.warn("Skipping {} #{}, it's running".format(prof, prof.numbers[profile.Profile]))
+                continue
+            
             thread_key = prof.thread_key
             to_graph = defaultdict(list)
 
-            
             if show_keys:
                 per_thread = prof.all_per_thead_infos[0][thread_key]
                 log_user.info("Profile id={}".format(prof_id))
@@ -210,6 +215,9 @@ class cmd_profile_graph_plot (gdb.Command):
                 continue
 
             if prof_id == "all":
+                if to_graph[prof_type][0] is None:
+                    pass # what to do here ??
+
                 to_all_graph.append(to_graph[prof_type][0])
                 continue
             
@@ -281,3 +289,55 @@ def initialize():
     from .. import profile
     
     cmd_profile_graph()
+    cmd_profile_offline_graph()
+
+class  cmd_profile_offline_graph(gdb.Command):
+    def __init__ (self):
+        gdb.Command.__init__(self, "profile graph offline", gdb.COMMAND_OBSCURE, prefix=True)
+        
+    def invoke (self, args, from_tty):
+        import pygal
+
+        import fileinput
+
+        chart = pygal.Line()
+        names = []
+        do_div = False
+        
+        finput = fileinput.input()
+        for line in finput:
+            line = line[:-1]
+            
+            if not line:
+                finput.close()
+                break
+            
+            if line.endswith(" y2"):
+                secondary = True
+                line = line.replace(" y2", "")
+            elif line.endswith(" /"):
+                do_div = True
+                line = line.replace(" /", "")
+            else:
+                secondary = False
+
+            try:
+                name, values = line.split(" | ")
+            
+                cast = float if "." in values else int
+                values = list(map(cast, values.split(" ")))
+            except Exception as e:
+                log_user.warn("Parsing failed, the line above will be ignored ({})".format(e))
+                continue
+            if do_div is True:
+                do_div = values
+                continue
+            elif do_div:
+                values = [a/b for a, b in zip(values, do_div)]
+                
+            chart.add(name, values, secondary=secondary)
+            names.append(name)
+            
+        TARGET = '/tmp/chart.png'
+        chart.render_to_png(TARGET)
+        log_user.info("Chart of {} plotted into {}".format("/".join(names), TARGET))
diff --git a/model/profiling/interaction/summary.py b/model/profiling/interaction/summary.py
index 69799d2..75ec962 100644
--- a/model/profiling/interaction/summary.py
+++ b/model/profiling/interaction/summary.py
@@ -231,16 +231,18 @@ class InfoEntrySummary():
         
     def avg(self, value, prof):
         if isinstance(value, str): return
+        
         try:
-            prev, it = self.values["avg"]
+            (prev, _what_), it = self.values["avg"]
             it += 1
             # iterative mean http://www.heikohoffmann.de/htmlthesis/node134.html
             new = prev + float(value) - prev*1/it 
         except KeyError:  # first pass
             new = value
             it = 0
-        except:
+        except Exception as e:
             import pdb;pdb.set_trace()
+            return
         self.values["avg"] = new, it
             
 class cmd_profile_summ (gdb.Command):
diff --git a/model/task/environment/openmp/capture/iomp/kmpc_for_static.py b/model/task/environment/openmp/capture/iomp/kmpc_for_static.py
index 16a397c..b799239 100644
--- a/model/task/environment/openmp/capture/iomp/kmpc_for_static.py
+++ b/model/task/environment/openmp/capture/iomp/kmpc_for_static.py
@@ -50,11 +50,11 @@ class kmpc_for_static_fini_Breakpoint(OmpFunctionBreakpoint):
 
     def prepare_before (self):
         data = {}
-        worker = current_worker()
+        data["worker"] = worker = current_worker()
         
         data["loop"], data["iteration"] = loop, iteration \
             = representation.ForLoopJob.get_current_loop_of(worker)
-
+        
         if not loop:
             log.warn("spurious end")
             return
@@ -63,14 +63,20 @@ class kmpc_for_static_fini_Breakpoint(OmpFunctionBreakpoint):
         
         stop_after = loop.break_after_next
         loop.break_after_next = False
-        
+
+        data["old-scheduler-locking"] = gdb.parameter("scheduler-locking")
+        if stop_after:
+            gdb.execute("set scheduler-locking on")
+            
         return False, stop_after, data
         
     def prepare_after(self, data):
-        iteration = loop.working
-        log_user.info("Stopped after loop {} iteration {}-{}.".format(data["loop"],
-                                                                      data["iteration"][0],
-                                                                      data["iteration"][1]))
+        gdb.execute("set scheduler-locking {}".format(data["old-scheduler-locking"]))
+        log.warn("Stopped in worker {} after loop {} iteration {}-{}.".format(
+                data["worker"],
+                data["loop"],
+                data["iteration"][0],
+                data["iteration"][1]))
         
         return True
 
diff --git a/model/task/environment/openmp/interaction/loop.py b/model/task/environment/openmp/interaction/loop.py
index 6771e4c..eb8f1f9 100644
--- a/model/task/environment/openmp/interaction/loop.py
+++ b/model/task/environment/openmp/interaction/loop.py
@@ -50,7 +50,7 @@ class cmd_omp_loop_break (gdb.Command):
 class cmd_omp_loop_break_after_next (gdb.Command):
     def __init__ (self):
         gdb.Command.__init__(self, "omp loop break after_next", gdb.COMMAND_OBSCURE, prefix=1)
-
+        
     def invoke (self, args, from_tty):
         last_loop = representation.ForLoopJob.get_last_loop()
 
@@ -58,17 +58,19 @@ class cmd_omp_loop_break_after_next (gdb.Command):
             log_user.warn("Could not find any last loop")
             return
 
-        action = "Enable" if not self.break_after_next else "Disable"
-            
-        log_user.info("{} break-after-next breakpoint on loop {}.".format(action, last_loop))
-        self.break_after_next = not self.break_after_next
+        action = "Enable" if not last_loop.break_after_next else "Disable"
 
-iteration_profiling_enabled = False
+        log_user.info("{} break-after-next breakpoint on loop {}.".format(action, repr(last_loop)))
+        last_loop.break_after_next = not last_loop.break_after_next
 
+iteration_profiling_enabled = False
 
 class cmd_omp_loop_profile (gdb.Command):
     def __init__ (self):
         gdb.Command.__init__(self, "omp loop profile", gdb.COMMAND_OBSCURE, prefix=True)
+
+    def invoke (self, arg, from_tty):
+        log_user.warn("Please specify what kind of profiling you want to do.")
         
 class cmd_omp_loop_profile_iterations (gdb.Command):
     def __init__ (self):
diff --git a/model/task/environment/openmp/interaction/profile.py b/model/task/environment/openmp/interaction/profile.py
index 13e5b45..c6674e2 100644
--- a/model/task/environment/openmp/interaction/profile.py
+++ b/model/task/environment/openmp/interaction/profile.py
@@ -1,3 +1,6 @@
+import logging; log = logging.getLogger(__name__)
+log_user = logging.getLogger("mcgdb.log.user.openmp.profile")
+
 import gdb
 
 from mcgdb.toolbox import my_gdb
@@ -15,4 +18,5 @@ class cmd_omp_profile (gdb.Command):
     def __init__ (self):
         gdb.Command.__init__(self, "omp profile", gdb.COMMAND_OBSCURE, prefix=True)
 
-        
+    def invoke (self, arg, from_tty):
+        log_user.warn("Please specify what kind of profiling you want to do.")
-- 
GitLab