Skip to content
Open
Show file tree
Hide file tree
Changes from 91 commits
Commits
Show all changes
112 commits
Select commit Hold shift + click to select a range
246a4c1
Initial profiler implementation (non working)
ChrisPaulBennett Jan 7, 2025
9727be9
CPU/Memory Logging working
ChrisPaulBennett Feb 24, 2025
171f7ee
GH Actions: use explicit `bash` shell & other defaults
MetRonnie Mar 12, 2025
7d50d0b
host-select: fix compatibility with force-condemned hosts
oliver-sanders Feb 19, 2025
8608088
Update cylc/flow/cfgspec/globalcfg.py
oliver-sanders Mar 18, 2025
060056b
actions: update build action to support python 3.13
oliver-sanders Mar 21, 2025
a0cdcd6
host-select: fix compatibility with force-condemned hosts
oliver-sanders Feb 19, 2025
3d4a187
Update cylc/flow/cfgspec/globalcfg.py
oliver-sanders Mar 18, 2025
3057edd
tests: convert unittest to pytest
oliver-sanders Mar 13, 2025
a6ecd29
tests/u: test_subprocpool.py::test_run_command_writes_to_err
oliver-sanders Mar 13, 2025
e5119b4
Time Series now working
ChrisPaulBennett Mar 4, 2025
9f593fc
Profiler sends KB instead of bytes
ChrisPaulBennett Mar 12, 2025
5f84c2f
Modifying unit tests
ChrisPaulBennett Mar 14, 2025
47a4a98
host-select: fix compatibility with force-condemned hosts
oliver-sanders Feb 19, 2025
356abff
Update cylc/flow/cfgspec/globalcfg.py
oliver-sanders Mar 18, 2025
6ed1770
tests: convert unittest to pytest
oliver-sanders Mar 13, 2025
4cfcffe
tests/u: test_subprocpool.py::test_run_command_writes_to_err
oliver-sanders Mar 13, 2025
1e5b804
Linting
ChrisPaulBennett Mar 21, 2025
38c31f5
Fail gracefully if cgroups cannot be found
ChrisPaulBennett Mar 24, 2025
339af9c
tests: convert unittest to pytest
oliver-sanders Mar 13, 2025
391559b
tests/u: test_subprocpool.py::test_run_command_writes_to_err
oliver-sanders Mar 13, 2025
b9c080c
Adding profiler unit tests
ChrisPaulBennett Mar 28, 2025
7091711
adding pycharm files to .gitignore file
ChrisPaulBennett Apr 1, 2025
ad6b3a1
Fixing my terrible rebasing
ChrisPaulBennett Apr 2, 2025
5bddeef
Merge remote-tracking branch 'refs/remotes/origin/master' into cylc_p…
ChrisPaulBennett Apr 2, 2025
afcdd81
MyPy Linting
ChrisPaulBennett Apr 2, 2025
378dcb9
More unit tests
ChrisPaulBennett Apr 2, 2025
ca1e796
Linting
ChrisPaulBennett Apr 2, 2025
58fae4f
Adding towncrier fragment
ChrisPaulBennett Apr 2, 2025
64e9b2b
Review changes
ChrisPaulBennett Apr 7, 2025
07348b7
Review changes
ChrisPaulBennett Apr 7, 2025
b485fd7
Review Changes
ChrisPaulBennett Apr 7, 2025
5793b23
Review Changes
ChrisPaulBennett Apr 8, 2025
73545ac
Review Changes
ChrisPaulBennett Apr 8, 2025
bf1b9c9
Review Changes
ChrisPaulBennett Apr 10, 2025
30d4382
profiler: add functional test for cgroup profiling
oliver-sanders Apr 10, 2025
49fcbc8
Review Changes
ChrisPaulBennett Apr 7, 2025
c63a250
Added polling interval configuration
ChrisPaulBennett Apr 15, 2025
939e128
Update unit tests
ChrisPaulBennett Apr 15, 2025
4928405
Added name to CONTRIBUTING.md
ChrisPaulBennett Apr 16, 2025
66acd1f
Added name to .mailmap
ChrisPaulBennett Apr 17, 2025
935fdc6
Merge remote-tracking branch 'Oliver/profiler' into cylc_profiler
ChrisPaulBennett Apr 22, 2025
cacf077
Fixed syntax error
ChrisPaulBennett Apr 22, 2025
99f9ae5
Fixed syntax error
ChrisPaulBennett Apr 23, 2025
a542523
Fixed the issue where CPU / Max RSS data is not available in the even…
ChrisPaulBennett Apr 24, 2025
1a63365
Update cylc/flow/cfgspec/globalcfg.py
ChrisPaulBennett Apr 24, 2025
048606b
Refactored max rss and cpu time data flow
ChrisPaulBennett Apr 28, 2025
b05c38d
Updating unit tests
ChrisPaulBennett Apr 28, 2025
80963cd
Linting
ChrisPaulBennett Apr 28, 2025
5d3cb0c
Linting
ChrisPaulBennett May 19, 2025
f51794b
Refactoring so that the jq command is not used
ChrisPaulBennett Jun 20, 2025
27a0879
Shellchecker linting
ChrisPaulBennett Jun 20, 2025
6efc7cf
Linting
ChrisPaulBennett Jul 8, 2025
9a5a4fa
Removing json usage
ChrisPaulBennett Jul 8, 2025
5a620ed
Merge remote-tracking branch 'ChrisPaulBennett/cylc_profiler' into cy…
ChrisPaulBennett Jul 8, 2025
f43bd83
Merge branch 'refs/heads/master' into cylc_profiler
ChrisPaulBennett Jul 8, 2025
8f7c419
Adding e2e functional tests
ChrisPaulBennett Jul 9, 2025
6f8c3f3
Linting
ChrisPaulBennett Jul 9, 2025
2e02286
Linting
ChrisPaulBennett Jul 9, 2025
2923917
Fixing functional tests
ChrisPaulBennett Jul 9, 2025
ce4122b
Merge remote-tracking branch 'refs/remotes/origin/master' into cylc_p…
ChrisPaulBennett Jul 9, 2025
15c29fa
Kill profiler more reliably
ChrisPaulBennett Jul 9, 2025
c09aa6f
Added unit test coverage
ChrisPaulBennett Jul 10, 2025
a29cf82
linting
ChrisPaulBennett Jul 10, 2025
172b453
linting
ChrisPaulBennett Jul 10, 2025
59200fc
Corrected cgroup versions
ChrisPaulBennett Jul 21, 2025
35daea9
Merge branch 'refs/heads/master' into cylc_profiler
ChrisPaulBennett Jul 21, 2025
9189ac3
Updating unit tests
ChrisPaulBennett Jul 25, 2025
31f2c18
Updating unit tests
ChrisPaulBennett Jul 25, 2025
baaa1a6
Merge branch 'refs/heads/master' into cylc_profiler
ChrisPaulBennett Jul 28, 2025
038348a
Updating unit tests
ChrisPaulBennett Jul 28, 2025
2975ea4
Updating unit tests
ChrisPaulBennett Jul 29, 2025
0448e70
Merge branch 'master' into cylc_profiler
ChrisPaulBennett Jul 29, 2025
3cff4a1
Merge branch 'master' into cylc_profiler
ChrisPaulBennett Jul 29, 2025
c8f3ab5
Merge remote-tracking branch 'ChrisPaulBennett/cylc_profiler' into cy…
ChrisPaulBennett Jul 29, 2025
5e994fb
updating .mailmap
ChrisPaulBennett Jul 29, 2025
5c8585e
Updating .mailmap
ChrisPaulBennett Aug 1, 2025
0c68cec
testing macos
ChrisPaulBennett Aug 4, 2025
7eddcf2
testing macos
ChrisPaulBennett Aug 5, 2025
2cd9991
testing macos
ChrisPaulBennett Aug 5, 2025
c1a5686
testing macos
ChrisPaulBennett Aug 5, 2025
2bc9ec6
testing macos
ChrisPaulBennett Aug 5, 2025
8ca760d
testing macos
ChrisPaulBennett Aug 5, 2025
ebbb8f0
testing macos
ChrisPaulBennett Aug 5, 2025
6f65ebd
testing macos
ChrisPaulBennett Aug 6, 2025
54553c1
testing macos
ChrisPaulBennett Aug 6, 2025
556f3ba
testing macos
ChrisPaulBennett Aug 6, 2025
f70c1d3
testing macos
ChrisPaulBennett Aug 6, 2025
1e93434
change how profiler is killed to be POSIX compliant
ChrisPaulBennett Oct 20, 2025
797b8ae
Added recording of memory allocation
ChrisPaulBennett Oct 20, 2025
5bfaf05
Updating unit tests
ChrisPaulBennett Oct 20, 2025
3c32069
profiler: remove global variables
oliver-sanders Oct 21, 2025
8592107
Refactored to global variables
ChrisPaulBennett Oct 23, 2025
6c95914
Code review changes
ChrisPaulBennett Oct 23, 2025
e30a1a4
Linting
ChrisPaulBennett Oct 23, 2025
c52f7fb
Linting
ChrisPaulBennett Oct 29, 2025
19ba481
Updating unit tests
ChrisPaulBennett Oct 29, 2025
48bb9c4
Linting
ChrisPaulBennett Oct 29, 2025
fb0a156
Adding unit test coverage
ChrisPaulBennett Oct 29, 2025
ba94659
Adding unit test coverage
ChrisPaulBennett Oct 29, 2025
ed7f4fc
Adding unit test coverage
ChrisPaulBennett Oct 29, 2025
5de1e27
Adding unit test coverage
ChrisPaulBennett Oct 29, 2025
2762d56
Linting
ChrisPaulBennett Oct 29, 2025
e1978c1
Linting
ChrisPaulBennett Oct 31, 2025
61f0ceb
Unit test coverage
ChrisPaulBennett Oct 31, 2025
2b27c25
Linting
ChrisPaulBennett Oct 31, 2025
31fa1fb
Merge remote-tracking branch 'refs/remotes/origin/master' into cylc_p…
ChrisPaulBennett Nov 10, 2025
f26ed40
Adding test coverage
ChrisPaulBennett Nov 19, 2025
80c38d0
Linting
ChrisPaulBennett Nov 20, 2025
c54cff5
Implemented usage of CylcError
ChrisPaulBennett Nov 20, 2025
f5e5af8
Linting
ChrisPaulBennett Nov 20, 2025
1598cd3
Fix functional tests
ChrisPaulBennett Nov 21, 2025
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ __pycache__/
# vscode
.vscode

# pycharm
.idea

# processed workflow configs
*.rc.processed
*.cylc.processed
Expand Down
1 change: 1 addition & 0 deletions .mailmap
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,5 @@ github-actions[bot] <[email protected]>
github-actions[bot] <[email protected]> GitHub Action
Diquan Jabbour <[email protected]>
Maxime Rio <[email protected]>
Christopher Bennett <[email protected]> ChrisPaulBennett <[email protected]>
Christopher Bennett <[email protected]> christopher.bennett <[email protected]>
1 change: 1 addition & 0 deletions changes.d/6663.feat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Adding CPU time and Max RSS to Analysis Tools
22 changes: 22 additions & 0 deletions cylc/flow/cfgspec/globalcfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -1395,6 +1395,28 @@ def default_for(

.. versionadded:: 8.0.0
''')

with Conf('profile'):
Conf('activate', VDR.V_BOOLEAN, False, desc='''
A Boolean that sets if the cylc profiler will be used

.. versionadded:: 8.0.0
''')
Conf('cgroups path', VDR.V_STRING,
default='/sys/fs/cgroup',
desc='''
The path to the cgroups filesystem. The default value
(/sys/fs/cgroup) is the standard location for cgroups on
linux and should work in most circumstances''')
Conf('polling interval', VDR.V_INTEGER,
default=10,
desc='''
The interval (in seconds) at which the profiler will
poll the cgroups filesystem for resource usage data.
The default value of 10 seconds should be sufficient for
most use cases, but can be adjusted as needed.
''')

Conf('job runner', VDR.V_STRING, 'background', desc=f'''
The system used to run jobs on the platform.

Expand Down
21 changes: 21 additions & 0 deletions cylc/flow/etc/job.sh
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ cylc__job__main() {
mkdir -p "$(dirname "${CYLC_TASK_WORK_DIR}")" || true
mkdir -p "${CYLC_TASK_WORK_DIR}"
cd "${CYLC_TASK_WORK_DIR}"

if [[ "${CYLC_PROFILE}" == "True" ]] ; then
cylc profile -m "${CYLC_CGROUP}" -i "${CYLC_POLLING_INTERVAL}" &
export profiler_pid="$!"
fi

# Env-Script, User Environment, Pre-Script, Script and Post-Script
# Run user scripts in subshell to protect cylc job script from interference.
# Waiting on background process allows signal traps to trigger immediately.
Expand All @@ -157,11 +163,15 @@ cylc__job__main() {
cylc__set_return "$ret_code"
fi
}
# Grab the max rss and cpu_time and clean up before changing directory
cylc__kill_profiler
# Empty work directory remove
cd
rmdir "${CYLC_TASK_WORK_DIR}" 2>'/dev/null' || true
# Send task succeeded message

wait "${CYLC_TASK_MESSAGE_STARTED_PID}" 2>'/dev/null' || true

cylc message -- "${CYLC_WORKFLOW_ID}" "${CYLC_TASK_JOB}" 'succeeded' || true
# (Ignore shellcheck "globbing and word splitting" warning here).
# shellcheck disable=SC2086
Expand All @@ -187,6 +197,14 @@ cylc__set_return() {
return "${1:-0}"
}

###############################################################################
# Save the data using cylc message and exit the profiler
cylc__kill_profiler() {
if [[ -n "${profiler_pid:-}" ]] && ps -p "$profiler_pid" > /dev/null; then
kill -s SIGINT "${profiler_pid}" || true
fi
}

###############################################################################
# Disable selected or all (if no arguments given) fail traps.
# Globals:
Expand Down Expand Up @@ -268,6 +286,9 @@ cylc__job_finish_err() {
# (Ignore shellcheck "globbing and word splitting" warning here).
# shellcheck disable=SC2086
trap '' ${CYLC_VACATION_SIGNALS:-} ${CYLC_FAIL_SIGNALS}

cylc__kill_profiler

if [[ -n "${CYLC_TASK_MESSAGE_STARTED_PID:-}" ]]; then
wait "${CYLC_TASK_MESSAGE_STARTED_PID}" 2>'/dev/null' || true
fi
Expand Down
12 changes: 10 additions & 2 deletions cylc/flow/job_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,16 @@ def _write_task_environment(self, handle, job_conf):
'\n export CYLC_TASK_TRY_NUMBER=%s' % job_conf['try_num'])
handle.write(
"\n export CYLC_TASK_FLOW_NUMBERS="
f"{','.join(str(f) for f in job_conf['flow_nums'])}"
)
f"{','.join(str(f) for f in job_conf['flow_nums'])}")
handle.write(
"\n export CYLC_PROFILE="
f"{job_conf['platform']['profile']['activate']}")
handle.write(
"\n export CYLC_CGROUP="
f"{job_conf['platform']['profile']['cgroups path']}")
handle.write(
"\n export CYLC_POLLING_INTERVAL="
f"{job_conf['platform']['profile']['polling interval']}")
# Standard parameter environment variables
for var, val in job_conf['param_var'].items():
handle.write('\n export CYLC_TASK_PARAM_%s="%s"' % (var, val))
Expand Down
276 changes: 276 additions & 0 deletions cylc/flow/scripts/profiler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
#!/usr/bin/env python3
# THIS FILE IS PART OF THE CYLC WORKFLOW ENGINE.
# Copyright (C) NIWA & British Crown (Met Office) & Contributors.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""cylc profiler [OPTIONS]
Profiler which periodically polls cgroups to track
the resource usage of jobs running on the node.
"""

import os
import re
import sys
import time
import signal
import asyncio
from pathlib import Path
from dataclasses import dataclass
from cylc.flow.terminal import cli_function
from cylc.flow.network.client_factory import get_client
from cylc.flow.option_parsers import CylcOptionParser as COP

INTERNAL = True
PID_REGEX = re.compile(r"([^:]*\d{6,}.*)")
RE_INT = re.compile(r'\d+')
max_rss_location = None
cpu_time_location = None
memory_allocated_path = None
cgroup_version = None
comms_timeout = None


def get_option_parser() -> COP:
parser = COP(
__doc__,
comms=True,
argdoc=[
],
)
parser.add_option(
"-i", type=int,
help="interval between query cycles in seconds", dest="delay")
parser.add_option(
"-m", type=str, help="Location of cgroups directory",
dest="cgroup_location")

return parser


@cli_function(get_option_parser)
def main(parser: COP, options) -> None:
"""CLI main."""
global comms_timeout
# Register the stop_profiler function with the signal library
signal.signal(signal.SIGINT, stop_profiler)
signal.signal(signal.SIGHUP, stop_profiler)
signal.signal(signal.SIGTERM, stop_profiler)

comms_timeout = options.comms_timeout

get_config(options)


@dataclass
class Process:
"""Class for representing CPU and Memory usage of a process"""
cgroup_memory_path: str
cgroup_cpu_path: str
memory_allocated_path: str


def stop_profiler(*args):
"""This function will be executed when the SIGINT signal is sent
to this process"""
# If a task fails instantly, or finishes very quickly (< 1 second),
# the get config function doesn't have time to run
if (max_rss_location is None
or cpu_time_location is None
or cgroup_version is None):
max_rss = 0
cpu_time = 0
memory_allocated = 0
else:
max_rss = parse_memory_file(max_rss_location, cgroup_version)
cpu_time = parse_cpu_file(cpu_time_location, cgroup_version)
memory_allocated = parse_memory_allocated(memory_allocated_path, cgroup_version)

GRAPHQL_MUTATION = """
mutation($WORKFLOWS: [WorkflowID]!,
$MESSAGES: [[String]], $JOB: String!, $TIME: String) {
message(workflows: $WORKFLOWS, messages:$MESSAGES,
taskJob:$JOB, eventTime:$TIME) {
result
}
}
"""

GRAPHQL_REQUEST_VARIABLES = {
"WORKFLOWS": [os.environ.get('CYLC_WORKFLOW_ID')],
"MESSAGES": [["DEBUG", f"cpu_time {cpu_time} max_rss {max_rss} mem_alloc {memory_allocated}"]],
"JOB": os.environ.get('CYLC_TASK_JOB'),
"TIME": "now"
}

pclient = get_client(os.environ.get('CYLC_WORKFLOW_ID'),
timeout=comms_timeout)

async def send_cylc_message():
await pclient.async_request(
'graphql',
{'request_string': GRAPHQL_MUTATION,
'variables': GRAPHQL_REQUEST_VARIABLES},
)

asyncio.run(send_cylc_message())
sys.exit(0)


def parse_memory_file(cgroup_memory_path, cgroup_version):
"""Open the memory stat file and copy the appropriate data"""

cgroup_memory_path = Path(cgroup_memory_path)

if cgroup_version == 2:
with open(cgroup_memory_path, 'r') as f:
for line in f:
if "anon" in line:
return int(''.join(filter(str.isdigit, line)))
else:
with open(cgroup_memory_path, 'r') as f:
for line in f:
return int(line)


def parse_memory_allocated(cgroup_memory_path, cgroup_version) -> int:
"""Open the memory stat file and copy the appropriate data"""

if cgroup_version == 2:
cgroup_memory_path = Path(cgroup_memory_path)

for i in range(5):
with open(cgroup_memory_path / "memory.max", 'r') as f:
line = f.readline()
if "max" not in line:
return int(line)
cgroup_memory_path = cgroup_memory_path.parent
if i == 5:
break
elif cgroup_version == 1:
return 0 # Memory limit not tracked for cgroups v1

raise FileNotFoundError("Could not find memory.max file")


def parse_cpu_file(cgroup_cpu_path, cgroup_version) -> int:
"""Open the memory stat file and return the appropriate data"""

if cgroup_version == 2:
with open(cgroup_cpu_path, 'r') as f:
for line in f:
if "usage_usec" in line:
return int(RE_INT.findall(line)[0]) // 1000
raise ValueError("Unable to find cpu usage data")
else:
with open(cgroup_cpu_path, 'r') as f:
for line in f:
# Cgroups v2 uses nanoseconds
return int(line) // 1000000
raise ValueError("Unable to find cpu usage data")


def get_cgroup_version(cgroup_location: str, cgroup_name: str) -> int:
global cgroup_version
if Path.exists(Path(cgroup_location + cgroup_name)):
cgroup_version = 2
return cgroup_version
elif Path.exists(Path(cgroup_location + "/memory" + cgroup_name)):
cgroup_version = 1
return cgroup_version
else:
raise FileNotFoundError("Cgroup not found at " +
cgroup_location + cgroup_name)


def get_cgroup_name() -> str:
"""Get the cgroup directory for the current process"""

# fugly hack to allow functional tests to use test data
if 'profiler_test_env_var' in os.environ:
return os.getenv('profiler_test_env_var')

# Get the PID of the current process
pid = os.getpid()
try:
# Get the cgroup information for the current process
with open('/proc/' + str(pid) + '/cgroup', 'r') as f:
result = f.read()
result = PID_REGEX.search(result).group()
return result
except FileNotFoundError as err:
raise FileNotFoundError(
'/proc/' + str(pid) + '/cgroup not found') from err
Copy link
Member

@oliver-sanders oliver-sanders Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is catching a FileNotFoundError and raising a near identical FileNotFoundError in it's place.

I think the intention was to replace a scary looking traceback with a more informative error. If so, try this out (note you'll likely need to import cylc.exceptions.CylcError first):

     try:
         # Get the cgroup information for the current process
         with open('/proc/' + str(pid) + '/cgroup', 'r') as f:
             result = f.read()
         result = PID_REGEX.search(result).group()
         return result
     except FileNotFoundError as err:
-        raise FileNotFoundError(
-            '/proc/' + str(pid) + '/cgroup not found') from err
+        raise CylcError('CGroup file not found: {err}') from None
  • CylcError is the class that (almost) all Cylc exceptions inherit from.
  • tldr; If you want a short clean error message, use CylcError or a subclass of it. If you want a scary traceback, use a plain exception.
  • CylcErrors get special treatment, the str(exc) gets written to stderr in red text. The traceback is not displayed unless running with --debug.
  • Note the from None hides the parent exception, preventing it from appearing in the traceback.


except AttributeError as err:
raise AttributeError("No cgroup found for process:", pid) from err


def get_cgroup_paths(version, location, name) -> Process:
global max_rss_location
global cpu_time_location
global memory_allocated_path

if version == 2:
max_rss_location = location + name + "/" + "memory.stat"
cpu_time_location = location + name + "/" + "cpu.stat"
memory_allocated_path = location + name
return Process(
cgroup_memory_path=location +
name + "/" + "memory.stat",
cgroup_cpu_path=location +
name + "/" + "cpu.stat",
memory_allocated_path=location + name)

elif version == 1:
max_rss_location = (location + "/memory" +
name + "/memory.max_usage_in_bytes")
cpu_time_location = (location + "/cpu" +
name + "/cpuacct.usage")
memory_allocated_path = location + name + "/" + "memory.limit_in_bytes"
return Process(
cgroup_memory_path=location + "/memory" +
name + "/memory.max_usage_in_bytes",
cgroup_cpu_path=location + "/cpu" +
name + "/cpuacct.usage",
memory_allocated_path="")

raise ValueError("Unable to determine cgroup version")


def profile(process, version, delay, keep_looping=lambda: True):
# The infinite loop that will constantly poll the cgroup
# The lambda function is used to allow the loop to be stopped in unit tests

while keep_looping():
# Write cpu / memory usage data to disk
# CPU_TIME = parse_cpu_file(process.cgroup_cpu_path, version)
time.sleep(delay)


def get_config(args):
# Find the cgroup that this process is running in.
# Cylc will put this profiler in the same cgroup
# as the job it is profiling
cgroup_name = get_cgroup_name()
cgroup_version = get_cgroup_version(args.cgroup_location, cgroup_name)
process = get_cgroup_paths(cgroup_version,
args.cgroup_location,
cgroup_name)
profile(process, cgroup_version, args.delay)


if __name__ == "__main__":
arg_parser = get_option_parser()
get_config(arg_parser.parse_args([]))
Loading
Loading