Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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-*/


52 changes: 34 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -131,7 +132,22 @@ Executes a cell with Score-P, i.e. it calls `python -m scorep <cell code>`

![](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.
Expand Down Expand Up @@ -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

Expand All @@ -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.
Expand Down
52 changes: 50 additions & 2 deletions src/scorep_jupyter/kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -861,6 +897,18 @@ 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("%%enable_vampir_launch_on_scorep_instrumented"):
self.launch_vampir_requested = True
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
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:
Expand Down
15 changes: 14 additions & 1 deletion src/scorep_jupyter/kernel_messages.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import os
from enum import Enum, auto

from .logging_config import LOGGING
Expand All @@ -12,6 +11,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 = {
Expand All @@ -38,6 +39,18 @@ 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. Add it to PATH to enable automatic launch'
' (e.g. in ~/.bashrc: export PATH="/path/to/vampir/bin:$PATH"'
),

KernelErrorCode.VAMPIR_LAUNCH_FAILED: (
"Failed to launch Vampir: {exception}"
),
}


Expand Down
2 changes: 0 additions & 2 deletions src/scorep_jupyter/logging_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
2 changes: 2 additions & 0 deletions tests/test_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down