diff --git a/gprofiler/consts.py b/gprofiler/consts.py index 31c5edf09..fddbf5b05 100644 --- a/gprofiler/consts.py +++ b/gprofiler/consts.py @@ -1,2 +1 @@ -CPU_PROFILING_MODE = "cpu" -DEFAULT_PROFILING_MODE = CPU_PROFILING_MODE +from granulate_utils.gprofiler.consts import * # noqa: F403,F401 diff --git a/gprofiler/exceptions.py b/gprofiler/exceptions.py index 254ac1854..1a00f7da9 100644 --- a/gprofiler/exceptions.py +++ b/gprofiler/exceptions.py @@ -1,109 +1 @@ -# -# Copyright (C) 2022 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import signal -import subprocess -from typing import List, Union - - -class PerfNoSupportedEvent(Exception): - pass - - -class StopEventSetException(Exception): - pass - - -class ProcessStoppedException(Exception): - pass - - -class CalledProcessError(subprocess.CalledProcessError): - # Enough characters for 200 long lines - MAX_STDIO_LENGTH = 120 * 200 - - def __init__( - self, - returncode: int, - cmd: Union[str, List[str]], - output: str, - stderr: str, - ): - assert isinstance(returncode, int), returncode - assert isinstance(cmd, str) or all(isinstance(s, str) for s in cmd), cmd - assert output is None or isinstance(output, str), output - assert stderr is None or isinstance(stderr, str), stderr - super().__init__(returncode, cmd, output, stderr) - - def _truncate_stdio(self, stdio: str) -> str: - if len(stdio) > self.MAX_STDIO_LENGTH: - stdio = stdio[: self.MAX_STDIO_LENGTH - 3] + "..." - return stdio - - def __str__(self) -> str: - if self.returncode and self.returncode < 0: - try: - base = f"Command {self.cmd!r} died with {signal.Signals(-self.returncode)!r}." - except ValueError: - base = f"Command {self.cmd!r} died with unknown signal {-self.returncode}." - else: - base = f"Command {self.cmd!r} returned non-zero exit status {self.returncode}." - return f"{base}\nstdout: {self._truncate_stdio(self.stdout)}\nstderr: {self._truncate_stdio(self.stderr)}" - - -class CalledProcessTimeoutError(CalledProcessError): - def __init__( - self, - timeout: float, - returncode: int, - cmd: Union[str, List[str]], - output: str, - stderr: str, - ): - super().__init__(returncode, cmd, output, stderr) - self.timeout = timeout - - def __str__(self) -> str: - return f"Timed out after {self.timeout} seconds\n" + super().__str__() - - -class ProgramMissingException(Exception): - def __init__(self, program: str): - super().__init__(f"The program {program!r} is missing! Please install it") - - -class APIError(Exception): - def __init__(self, message: str, full_data: dict = None): - self.message = message - self.full_data = full_data - - def __str__(self) -> str: - return self.message - - -class ThreadStopTimeoutError(Exception): - pass - - -class SystemProfilerStartFailure(Exception): - pass - - -class NoProfilersEnabledError(Exception): - pass - - -class NoRwExecDirectoryFoundError(Exception): - pass +from granulate_utils.gprofiler.exceptions import * # noqa: F403,F401 diff --git a/gprofiler/platform.py b/gprofiler/platform.py index 9635a3177..f328a35af 100644 --- a/gprofiler/platform.py +++ b/gprofiler/platform.py @@ -1,30 +1 @@ -# -# Copyright (C) 2022 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -import sys -from functools import lru_cache - -WINDOWS_PLATFORM_NAME = "win32" -LINUX_PLATFORM_NAME = "linux" - - -@lru_cache(maxsize=None) -def is_windows() -> bool: - return sys.platform == WINDOWS_PLATFORM_NAME - - -@lru_cache(maxsize=None) -def is_linux() -> bool: - return sys.platform == LINUX_PLATFORM_NAME +from granulate_utils.gprofiler.platform import * # noqa: F403,F401 diff --git a/gprofiler/utils/fs.py b/gprofiler/utils/fs.py index 974ebab14..f8e847535 100644 --- a/gprofiler/utils/fs.py +++ b/gprofiler/utils/fs.py @@ -1,109 +1 @@ -# -# Copyright (C) 2022 Intel Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import errno -import os -import shutil -from pathlib import Path -from secrets import token_hex -from typing import Union - -from gprofiler.platform import is_windows -from gprofiler.utils import is_root, remove_path, run_process - - -def safe_copy(src: str, dst: str) -> None: - """ - Safely copies 'src' to 'dst'. Safely means that writing 'dst' is performed at a temporary location, - and the file is then moved, making the filesystem-level change atomic. - """ - dst_tmp = f"{dst}.tmp" - shutil.copy(src, dst_tmp) - os.rename(dst_tmp, dst) - - -def is_rw_exec_dir(path: Path) -> bool: - """ - Is 'path' rw and exec? - """ - assert is_owned_by_root(path), f"expected {path} to be owned by root!" - - # randomize the name - this function runs concurrently on paths of in same mnt namespace. - test_script = path / f"t-{token_hex(10)}.sh" - - # try creating & writing - try: - test_script.write_text("#!/bin/sh\nexit 0") - test_script.chmod(0o755) # make sure it's executable. file is already writable only by root due to umask. - except OSError as e: - if e.errno == errno.EROFS: - # ro - return False - remove_path(test_script) - raise - - # try executing - try: - run_process([str(test_script)], suppress_log=True) - except PermissionError: - # noexec - return False - finally: - test_script.unlink() - - return True - - -def escape_filename(filename: str) -> str: - return filename.replace(":", "-" if is_windows() else ":") - - -def is_owned_by_root(path: Path) -> bool: - statbuf = path.stat() - return statbuf.st_uid == 0 and statbuf.st_gid == 0 - - -def mkdir_owned_root(path: Union[str, Path], mode: int = 0o755) -> None: - """ - Ensures a directory exists and is owned by root. - - If the directory exists and is owned by root, it is left as is. - If the directory exists and is not owned by root, it is removed and recreated. If after recreation - it is still not owned by root, the function raises. - """ - assert is_root() # this function behaves as we expect only when run as root - - path = path if isinstance(path, Path) else Path(path) - # parent is expected to be root - otherwise, after we create the root-owned directory, it can be removed - # as re-created as non-root by a regular user. - if not is_owned_by_root(path.parent): - raise Exception(f"expected {path.parent} to be owned by root!") - - if path.exists(): - if is_owned_by_root(path): - return - - shutil.rmtree(path) - - try: - os.mkdir(path, mode=mode) - except FileExistsError: - # likely racing with another thread of gprofiler. as long as the directory is root after all, we're good. - pass - - if not is_owned_by_root(path): - # lost race with someone else? - raise Exception(f"Failed to create directory {str(path)} as owned by root") +from granulate_utils.gprofiler.utils.fs import * # noqa: F403,F401