Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 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
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:-}" && -d "/proc/${profiler_pid}" ]]; then
Copy link
Member

Choose a reason for hiding this comment

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

The POSIX standard is a set of interfaces that all compliant operating systems must follow (Mac OS included).

The /proc filesystem ain't POSIX, it's a Linux specific thing, hence the fun you had with Mac OS testing.

The most POSIX way to determine if a process is running that I could find is ps -p:

Suggested change
if [[ -n "${profiler_pid:-}" && -d "/proc/${profiler_pid}" ]]; then
if [[ -n "${profiler_pid:-}" ]] && ps -p "$profiler_pid" >/dev/null;

You can see the POSIX description for the ps command here:

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html

The index of commands is here:

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/

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
236 changes: 236 additions & 0 deletions cylc/flow/scripts/profiler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
#!/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
cgroup_version = None
comms_timeout = None
Comment on lines +38 to +41
Copy link
Member

Choose a reason for hiding this comment

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

Unused global variables:

Suggested change
max_rss_location = None
cpu_time_location = 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
Comment on lines +64 to +70
Copy link
Member

Choose a reason for hiding this comment

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

Unused global variables:

Suggested change
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
# 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)


get_config(options)


@dataclass
class Process:
"""Class for representing CPU and Memory usage of a process"""
cgroup_memory_path: str
cgroup_cpu_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
else:
max_rss = parse_memory_file(max_rss_location)
cpu_time = parse_cpu_file(cpu_time_location, 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}"]],
"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):
"""Open the memory stat file and copy the appropriate data"""

with open(cgroup_memory_path, 'r') as f:
for line in f:
return int(line) // 1024


def parse_cpu_file(cgroup_cpu_path, cgroup_version):
"""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
elif cgroup_version == 1:
with open(cgroup_cpu_path, 'r') as f:
for line in f:
# Cgroups v2 uses nanoseconds
return int(line) / 1000000


def get_cgroup_version(cgroup_location: str, cgroup_name: str) -> int:
# HPC uses cgroups v2 and SPICE uses cgroups v1
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)
Comment on lines +151 to +161
Copy link
Member

Choose a reason for hiding this comment

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

Unused global variables:

(also Met Office specific comment)

Suggested change
# HPC uses cgroups v2 and SPICE uses cgroups v1
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)
if Path.exists(Path(cgroup_location + cgroup_name)):
return 2
elif Path.exists(Path(cgroup_location + "/memory" + cgroup_name)):
return 1
else:
raise FileNotFoundError("Cgroup not found at " +
cgroup_location + cgroup_name)



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

# fugly hack to allow functional tests to use test data
Copy link
Member

Choose a reason for hiding this comment

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

🤣

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

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


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

elif version == 1:
max_rss_location = (location + "/memory" +
name + "/memory.max_usage_in_bytes")
cpu_time_location = (location + "/cpu" +
name + "/cpuacct.usage")
return Process(
cgroup_memory_path=location + "/memory" +
name + "/memory.max_usage_in_bytes",
cgroup_cpu_path=location + "/cpu" +
name + "/cpuacct.usage")
Comment on lines +188 to +208
Copy link
Member

Choose a reason for hiding this comment

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

Unused global variables:

Suggested change
global max_rss_location
global cpu_time_location
if version == 2:
max_rss_location = location + name + "/" + "memory.peak"
cpu_time_location = location + name + "/" + "cpu.stat"
return Process(
cgroup_memory_path=location +
name + "/" + "memory.peak",
cgroup_cpu_path=location +
name + "/" + "cpu.stat")
elif version == 1:
max_rss_location = (location + "/memory" +
name + "/memory.max_usage_in_bytes")
cpu_time_location = (location + "/cpu" +
name + "/cpuacct.usage")
return Process(
cgroup_memory_path=location + "/memory" +
name + "/memory.max_usage_in_bytes",
cgroup_cpu_path=location + "/cpu" +
name + "/cpuacct.usage")
if version == 2:
return Process(
cgroup_memory_path=location +
name + "/" + "memory.peak",
cgroup_cpu_path=location +
name + "/" + "cpu.stat")
elif version == 1:
return Process(
cgroup_memory_path=location + "/memory" +
name + "/memory.max_usage_in_bytes",
cgroup_cpu_path=location + "/cpu" +
name + "/cpuacct.usage")

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These global variables are used. They are used in the stop_profiler function.
I can't see a way to pass it arguments when it is called by the signal library. Using globals is the only way I can see to do it



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([]))
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ cylc.command =
ping = cylc.flow.scripts.ping:main
play = cylc.flow.scripts.play:main
poll = cylc.flow.scripts.poll:main
profile = cylc.flow.scripts.profiler:main
psutils = cylc.flow.scripts.psutil:main
reinstall = cylc.flow.scripts.reinstall:main
release = cylc.flow.scripts.release:main
Expand Down
Loading
Loading