From 39de50ef76346c7109d6677b7c4781ac56b30aed Mon Sep 17 00:00:00 2001 From: OutlyingWest Date: Thu, 7 Aug 2025 14:39:58 +0200 Subject: [PATCH 1/8] vampir launch command added --- src/scorep_jupyter/kernel.py | 45 +++++++++++++++++++++++++-- src/scorep_jupyter/kernel_messages.py | 13 ++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/scorep_jupyter/kernel.py b/src/scorep_jupyter/kernel.py index 2258b81..37911c9 100644 --- a/src/scorep_jupyter/kernel.py +++ b/src/scorep_jupyter/kernel.py @@ -100,7 +100,7 @@ def __init__(self, **kwargs): importlib.import_module("scorep") except ModuleNotFoundError: self.scorep_python_available_ = False - + self.launch_vampir_requested = False logging.config.dictConfig(LOGGING) self.log = logging.getLogger("kernel") @@ -634,8 +634,12 @@ async def scorep_execute( f"Instrumentation results can be found in " f"{os.getcwd()}/{scorep_folder}" ) - self.pershelper.postprocess() + + # Optional Vampir launch + if self.launch_vampir_requested and scorep_folder: + self.try_launch_vampir(scorep_folder) + return self.standard_reply() def start_reading_scorep_process_streams( @@ -786,6 +790,38 @@ def handle_captured_output(self, output: List[str], stream: str): else: self.log.error(f"Undefined stream type: {stream}") + def try_launch_vampir(self, scorep_folder: str): + """ + Attempts to find traces.otf2 and launch Vampir on it. + Errors are logged using log_error(). + """ + trace_path = None + for root, dirs, files in os.walk(scorep_folder): + if "traces.otf2" in files: + trace_path = os.path.join(root, "traces.otf2") + break + + if not trace_path or not os.path.isfile(trace_path): + self.log_error( + KernelErrorCode.INSTRUMENTATION_PATH_UNKNOWN, + scorep_folder=scorep_folder, + ) + return + + if shutil.which("vampir") is None: + self.log_error(KernelErrorCode.VAMPIR_NOT_FOUND) + return + + try: + subprocess.run(["pkill", "-f", "vampir"], check=False) + self.cell_output(f"\nLaunching Vampir: {trace_path}") + subprocess.Popen(["vampir", trace_path]) + except Exception as e: + self.log_error( + KernelErrorCode.VAMPIR_LAUNCH_FAILED, + exception=str(e), + ) + async def do_execute( self, code, @@ -861,6 +897,11 @@ async def do_execute( return self.scorep_not_available() or self.abort_writefile() elif code.startswith("%%end_writefile"): return self.scorep_not_available() or self.end_writefile() + + elif code.startswith("%%launch_vampir_on_scorep_instrumented"): + self.launch_vampir_requested = True + self.cell_output("Vampir will be launched after next instrumented execution.") + return self.standard_reply() elif code.startswith("%%execute_with_scorep"): scorep_missing = self.scorep_not_available() if scorep_missing is None: diff --git a/src/scorep_jupyter/kernel_messages.py b/src/scorep_jupyter/kernel_messages.py index bd5a3fa..4b7e668 100644 --- a/src/scorep_jupyter/kernel_messages.py +++ b/src/scorep_jupyter/kernel_messages.py @@ -12,6 +12,8 @@ class KernelErrorCode(Enum): SCOREP_SUBPROCESS_FAIL = auto() SCOREP_NOT_AVAILABLE = auto() SCOREP_PYTHON_NOT_AVAILABLE = auto() + VAMPIR_NOT_FOUND = auto() + VAMPIR_LAUNCH_FAILED = auto() KERNEL_ERROR_MESSAGES = { @@ -38,6 +40,17 @@ class KernelErrorCode(Enum): "[mode: {mode}] Subprocess terminated unexpectedly. " "Persistence not recorded (marshaller: {marshaller})." ), + KernelErrorCode.INSTRUMENTATION_PATH_UNKNOWN: ( + "Instrumentation output directory not found or missing traces.otf2 " + "(looked in: {scorep_folder})" + ), + KernelErrorCode.VAMPIR_NOT_FOUND: ( + "Vampir binary not found in PATH. Cannot launch visualization." + ), + + KernelErrorCode.VAMPIR_LAUNCH_FAILED: ( + "Failed to launch Vampir: {exception}" + ), } From 3c24d57a6b5134f8ae89cc1182f62baf42272650 Mon Sep 17 00:00:00 2001 From: OutlyingWest Date: Thu, 7 Aug 2025 14:53:07 +0200 Subject: [PATCH 2/8] vampir disabling command added --- src/scorep_jupyter/kernel.py | 6 +++++- src/scorep_jupyter/logging_config.py | 2 -- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/scorep_jupyter/kernel.py b/src/scorep_jupyter/kernel.py index 37911c9..4f96234 100644 --- a/src/scorep_jupyter/kernel.py +++ b/src/scorep_jupyter/kernel.py @@ -898,10 +898,14 @@ async def do_execute( elif code.startswith("%%end_writefile"): return self.scorep_not_available() or self.end_writefile() - elif code.startswith("%%launch_vampir_on_scorep_instrumented"): + elif code.startswith("%%enable_vampir_launch_on_scorep_instrumented"): self.launch_vampir_requested = True self.cell_output("Vampir will be launched after next instrumented execution.") return self.standard_reply() + elif code.startswith("%%disable_vampir_launch"): + self.launch_vampir_requested = False + self.cell_output("Vampir launching disabled.") + return self.standard_reply() elif code.startswith("%%execute_with_scorep"): scorep_missing = self.scorep_not_available() if scorep_missing is None: diff --git a/src/scorep_jupyter/logging_config.py b/src/scorep_jupyter/logging_config.py index baed6b8..8fb8007 100644 --- a/src/scorep_jupyter/logging_config.py +++ b/src/scorep_jupyter/logging_config.py @@ -5,8 +5,6 @@ PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent -print(f"{Path(__file__).as_uri()=}") -print(f"{PROJECT_ROOT=}") LOGGING_DIR = PROJECT_ROOT / "logs_scorep_jupyter" os.makedirs(LOGGING_DIR, exist_ok=True) From 693cbff358bb5f78dacb1d70d998b427b3c3bd71 Mon Sep 17 00:00:00 2001 From: OutlyingWest Date: Thu, 7 Aug 2025 15:03:20 +0200 Subject: [PATCH 3/8] vampir launch control commands documented; logging chapter moved to Usage --- README.md | 52 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index f5b808f..9fdad2a 100644 --- a/README.md +++ b/README.md @@ -29,13 +29,14 @@ For binding to Score-P, the kernel uses the [Score-P Python bindings](https://gi - [Usage](#usage) - [Score-P Instrumentation](#score-p-instrumentation) - [Configuring Score-P in Jupyter](#configuring-score-p-in-jupyter) + - [Vampir Launch Control](#vampir-launch-control) - [Multi-Cell Mode](#multi-cell-mode) - [Write Mode](#write-mode) + - [Logging Configuration](#logging-configuration) - [Presentation of Performance Data](#presentation-of-performance-data) - [Limitations](#limitations) - [Serialization Type Support](#serialization-type-support) - [Overhead](#overhead) - - [Logging Configuration](#logging-configuration) - [Future Work](#future-work) - [Citing](#citing) - [Contact](#contact) @@ -131,7 +132,22 @@ Executes a cell with Score-P, i.e. it calls `python -m scorep ` ![](doc/instrumentation.gif) +### Vampir Launch Control + +To automatically launch **Vampir** after a cell with Score-P instrumentation, use: + +``` +%%enable_vampir_launch_on_scorep_instrumented +``` +This will cause the kernel to open `traces.otf2` in Vampir (if found) after the next instrumented cell. +To disable this behavior again: + +``` +%%disable_vampir_launch +``` + +By default, Vampir launching is disabled. You must enable it explicitly when needed. ## Multi-Cell Mode You can also treat multiple cells as one single cell by using the multi cell mode. Therefore you can mark the cells in the order you wish to execute them. @@ -186,6 +202,23 @@ Stops the marking process and writes the marked cells in a Python script. Additi ![](doc/writemode.gif) +## Logging Configuration +To adjust logging and obtain more detailed output about the behavior of the scorep_jupyter kernel, refer to the `src/logging_config.py` file. +This file contains configuration options for controlling the verbosity, format, and destination of log messages. You can customize it to suit your debugging needs. + +Log files are stored in the following directory: +``` +scorep_jupyter_kernel_python/ +├── logs_scorep_jupyter/ +│ ├── debug.log +│ ├── info.log +└── └── error.log +``` +In some cases, you may want to suppress tqdm messages that are saved to error.log (since tqdm outputs to stderr). This can be done using the following environment variable: +``` +%env TQDM_DISABLE=1 +``` + # Presentation of Performance Data @@ -205,23 +238,6 @@ Similar yields for cloudpickle. Use the `%%marshalling_settings` magic command t When dealing with big data structures, there might be a big runtime overhead at the beginning and the end of a Score-P cell. This is due to additional data saving and loading processes for persistency in the background. However this does not affect the actual user code and the Score-P measurements. -## Logging Configuration -To adjust logging and obtain more detailed output about the behavior of the scorep_jupyter kernel, refer to the `src/logging_config.py` file. -This file contains configuration options for controlling the verbosity, format, and destination of log messages. You can customize it to suit your debugging needs. - -Log files are stored in the following directory: -``` -scorep_jupyter_kernel_python/ -├── logs_scorep_jupyter/ -│ ├── debug.log -│ ├── info.log -└── └── error.log -``` -In some cases, you may want to suppress tqdm messages that are saved to error.log (since tqdm outputs to stderr). This can be done using the following environment variable: -``` -%env TQDM_DISABLE=1 -``` - # Future Work The kernel is still under development. From 08aa9c9aa0406644c5201af6c2e591a2418e030d Mon Sep 17 00:00:00 2001 From: OutlyingWest Date: Thu, 7 Aug 2025 15:08:39 +0200 Subject: [PATCH 4/8] tests fixed --- tests/test_kernel.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_kernel.py b/tests/test_kernel.py index 0aaab6a..51738d3 100644 --- a/tests/test_kernel.py +++ b/tests/test_kernel.py @@ -186,6 +186,8 @@ def test_error_templates_are_formatable(self): "detail": "dummy_detail", "step": "dummy_step", "optional_hint": "dummy_optional_hint", + "scorep_folder": "/fake/path/to/scorep-dir", + "exception": "dummy_exception", } for code, template in KERNEL_ERROR_MESSAGES.items(): From 4248559ee30d1df18affe5446ea1ee47aa8330a8 Mon Sep 17 00:00:00 2001 From: OutlyingWest Date: Thu, 7 Aug 2025 17:48:58 +0200 Subject: [PATCH 5/8] ignore scorep-* results --- .gitignore | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.gitignore b/.gitignore index dd4f63d..6315184 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,15 @@ __pycache__/ build logs_scorep_jupyter/ **/*.egg-info + +# PyCharm +.idea/ +.ipynb_checkpoints/ + +# Jupyter Notebook +.ipynb_checkpoints + +# Ignore all result directories inside examples/ at any depth +examples/**/scorep-*/ + + From fcc92dc834f0a94e05e894c62fbda02e00648514 Mon Sep 17 00:00:00 2001 From: OutlyingWest Date: Thu, 7 Aug 2025 17:50:26 +0200 Subject: [PATCH 6/8] VAMPIR_NOT_FOUND message at %%enable_vampir_launch_on_scorep_instrumented execution --- src/scorep_jupyter/kernel.py | 5 ++++- src/scorep_jupyter/kernel_messages.py | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/scorep_jupyter/kernel.py b/src/scorep_jupyter/kernel.py index 4f96234..6bcd488 100644 --- a/src/scorep_jupyter/kernel.py +++ b/src/scorep_jupyter/kernel.py @@ -900,7 +900,10 @@ async def do_execute( elif code.startswith("%%enable_vampir_launch_on_scorep_instrumented"): self.launch_vampir_requested = True - self.cell_output("Vampir will be launched after next instrumented execution.") + if shutil.which("vampir") is None: + self.log_error(KernelErrorCode.VAMPIR_NOT_FOUND) + else: + self.cell_output("Vampir will be launched after next instrumented execution.") return self.standard_reply() elif code.startswith("%%disable_vampir_launch"): self.launch_vampir_requested = False diff --git a/src/scorep_jupyter/kernel_messages.py b/src/scorep_jupyter/kernel_messages.py index 4b7e668..7eb8199 100644 --- a/src/scorep_jupyter/kernel_messages.py +++ b/src/scorep_jupyter/kernel_messages.py @@ -45,7 +45,8 @@ class KernelErrorCode(Enum): "(looked in: {scorep_folder})" ), KernelErrorCode.VAMPIR_NOT_FOUND: ( - "Vampir binary not found in PATH. Cannot launch visualization." + "Vampir binary not found in PATH. Add it to PATH to enable automatic launch" + " (e.g. via `%env PATH=/path/to/vampir/bin:$PATH`)." ), KernelErrorCode.VAMPIR_LAUNCH_FAILED: ( From 29a7570d377637a80f2d0d9f1815f7ad9cc7b4b0 Mon Sep 17 00:00:00 2001 From: OutlyingWest Date: Thu, 7 Aug 2025 18:13:03 +0200 Subject: [PATCH 7/8] %env unable to unwrap vars like '$PATH' -> not found PATH message fixed --- src/scorep_jupyter/kernel_messages.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scorep_jupyter/kernel_messages.py b/src/scorep_jupyter/kernel_messages.py index 7eb8199..6c47c65 100644 --- a/src/scorep_jupyter/kernel_messages.py +++ b/src/scorep_jupyter/kernel_messages.py @@ -45,8 +45,8 @@ class KernelErrorCode(Enum): "(looked in: {scorep_folder})" ), KernelErrorCode.VAMPIR_NOT_FOUND: ( - "Vampir binary not found in PATH. Add it to PATH to enable automatic launch" - " (e.g. via `%env PATH=/path/to/vampir/bin:$PATH`)." + 'Vampir binary not found in PATH. Add it to PATH to enable automatic launch' + ' (e.g. in ~/.bashrc: export PATH="/path/to/vampir/bin:$PATH"' ), KernelErrorCode.VAMPIR_LAUNCH_FAILED: ( From c6a8a455fc0b0c5d1d9da0556e9a116c582e05e3 Mon Sep 17 00:00:00 2001 From: OutlyingWest Date: Thu, 7 Aug 2025 18:50:07 +0200 Subject: [PATCH 8/8] os unused deleted --- src/scorep_jupyter/kernel_messages.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/scorep_jupyter/kernel_messages.py b/src/scorep_jupyter/kernel_messages.py index 6c47c65..d22cc78 100644 --- a/src/scorep_jupyter/kernel_messages.py +++ b/src/scorep_jupyter/kernel_messages.py @@ -1,4 +1,3 @@ -import os from enum import Enum, auto from .logging_config import LOGGING