Skip to content

Commit dd79714

Browse files
committed
Merge branch 'master' into grpcio-1-62-2
2 parents 2fb9611 + 076e9f2 commit dd79714

File tree

4 files changed

+55
-21
lines changed

4 files changed

+55
-21
lines changed

.github/workflows/build-test-deploy.yml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
cp output/gprofiler_x86_64 output/gprofiler # for backwards compatibility, we upload both with arch suffix and without
3636
3737
- name: Upload the executables as job artifacts
38-
uses: actions/upload-artifact@v2
38+
uses: actions/upload-artifact@v4
3939
with:
4040
name: gprofiler_x86_64
4141
path: output/
@@ -90,7 +90,7 @@ jobs:
9090
submodules: true
9191

9292
- name: Download the executable from previous job
93-
uses: actions/download-artifact@v2
93+
uses: actions/download-artifact@v4
9494
with:
9595
name: gprofiler_x86_64
9696
path: dist/
@@ -130,7 +130,7 @@ jobs:
130130
mv build/aarch64/gprofiler output/gprofiler_aarch64
131131
132132
- name: Upload the executables as job artifacts
133-
uses: actions/upload-artifact@v2
133+
uses: actions/upload-artifact@v4
134134
with:
135135
name: gprofiler_aarch64
136136
path: output/
@@ -155,13 +155,13 @@ jobs:
155155
run: ./scripts/verify_tag.sh
156156

157157
- name: Download x86_64 executable from a previous job
158-
uses: actions/download-artifact@v2
158+
uses: actions/download-artifact@v4
159159
with:
160160
name: gprofiler_x86_64
161161
path: output/
162162

163163
- name: Download aarch64 executable from a previous job
164-
uses: actions/download-artifact@v2
164+
uses: actions/download-artifact@v4
165165
with:
166166
name: gprofiler_aarch64
167167
path: output/
@@ -189,7 +189,7 @@ jobs:
189189
fetch-depth: 0
190190

191191
- name: Download executables from the previous job
192-
uses: actions/download-artifact@v2
192+
uses: actions/download-artifact@v4
193193
with:
194194
name: gprofiler_x86_64
195195
path: output/
@@ -205,7 +205,7 @@ jobs:
205205
run: mkdir -p output && docker image save gprofiler_x86_64 > output/gprofiler_x86_64.img
206206

207207
- name: Upload the image artifact
208-
uses: actions/upload-artifact@v2
208+
uses: actions/upload-artifact@v4
209209
with:
210210
name: gprofiler_x86_64.img
211211
path: output/
@@ -250,13 +250,13 @@ jobs:
250250
submodules: true
251251

252252
- name: Download the executable from previous job
253-
uses: actions/download-artifact@v2
253+
uses: actions/download-artifact@v4
254254
with:
255255
name: gprofiler_x86_64
256256
path: output/
257257

258258
- name: Download the image from previous job
259-
uses: actions/download-artifact@v2
259+
uses: actions/download-artifact@v4
260260
with:
261261
name: gprofiler_x86_64.img
262262
path: output/
@@ -309,7 +309,7 @@ jobs:
309309
uses: docker/setup-buildx-action@v1
310310

311311
- name: Download executables from the previous job
312-
uses: actions/download-artifact@v2
312+
uses: actions/download-artifact@v4
313313
with:
314314
name: gprofiler_aarch64
315315
path: output/
@@ -347,7 +347,7 @@ jobs:
347347
# build-container-aarch64 has pushed the image to DockerHub, so we'll pull it later when creating
348348
# the manifest.
349349
- name: Download the x86_64 image from previous job
350-
uses: actions/download-artifact@v2
350+
uses: actions/download-artifact@v4
351351
with:
352352
name: gprofiler_x86_64.img
353353
path: output/

gprofiler/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515
#
16-
__version__ = "1.49.0"
16+
__version__ = "1.50.0"

gprofiler/profilers/python_ebpf.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@
1717
import os
1818
import resource
1919
import selectors
20+
import shutil
2021
import signal
2122
from pathlib import Path
2223
from subprocess import Popen
23-
from typing import Dict, List, Optional, Tuple, cast
24+
from typing import Dict, List, Optional, Tuple, Union, cast
2425

2526
from granulate_utils.linux.ns import is_running_in_init_pid
2627
from psutil import NoSuchProcess, Process
@@ -86,6 +87,10 @@ def __init__(
8687
self._kernel_offsets: Dict[str, int] = {}
8788
self._metadata = python.PythonMetadata(self._profiler_state.stop_event)
8889
self._verbose = verbose
90+
self._pyperf_staticx_tmpdir: Optional[Path] = None
91+
if os.environ.get("TMPDIR", None) is not None:
92+
# We want to create a new level of hirerachy in our current staticx tempdir.
93+
self._pyperf_staticx_tmpdir = Path(os.environ["TMPDIR"]) / ("pyperf_" + random_prefix())
8994

9095
@classmethod
9196
def _check_output(cls, process: Popen, output_path: Path) -> None:
@@ -153,6 +158,14 @@ def _pyperf_base_command(self) -> List[str]:
153158
cmd.extend(["-v", "4"])
154159
return cmd
155160

161+
def _staticx_cleanup(self) -> None:
162+
"""
163+
We experienced issues with PyPerf's staticx'd directory, so we want to clean it up on termination.
164+
"""
165+
assert not self.is_running(), "_staticx_cleanup is impossible while PyPerf is running"
166+
if self._pyperf_staticx_tmpdir is not None and self._pyperf_staticx_tmpdir.exists():
167+
shutil.rmtree(self._pyperf_staticx_tmpdir.as_posix())
168+
156169
def test(self) -> None:
157170
self._ebpf_environment()
158171

@@ -170,14 +183,16 @@ def test(self) -> None:
170183
"--duration",
171184
"1",
172185
]
173-
process = start_process(cmd)
186+
process = start_process(cmd, tmpdir=self._pyperf_staticx_tmpdir)
174187
try:
175188
poll_process(process, self._POLL_TIMEOUT, self._profiler_state.stop_event)
176189
except TimeoutError:
177190
process.kill()
178191
raise
179192
else:
180193
self._check_output(process, self.output_path)
194+
finally:
195+
self._staticx_cleanup()
181196

182197
def start(self) -> None:
183198
logger.info("Starting profiling of Python processes with PyPerf")
@@ -201,7 +216,7 @@ def start(self) -> None:
201216
for f in glob.glob(f"{str(self.output_path)}.*"):
202217
os.unlink(f)
203218

204-
process = start_process(cmd)
219+
process = start_process(cmd, tmpdir=self._pyperf_staticx_tmpdir)
205220
# wait until the transient data file appears - because once returning from here, PyPerf may
206221
# be polled via snapshot() and we need it to finish installing its signal handler.
207222
try:
@@ -212,6 +227,7 @@ def start(self) -> None:
212227
stdout = process.stdout.read()
213228
stderr = process.stderr.read()
214229
logger.error("PyPerf failed to start", stdout=stdout, stderr=stderr)
230+
self._staticx_cleanup()
215231
raise
216232
else:
217233
self.process = process
@@ -303,16 +319,23 @@ def snapshot(self) -> ProcessToProfileData:
303319
return profiles
304320

305321
def _terminate(self) -> Tuple[Optional[int], str, str]:
322+
exit_status: Optional[int] = None
323+
stdout: Union[str, bytes] = ""
324+
stderr: Union[str, bytes] = ""
325+
306326
self._unregister_process_selectors()
307327
if self.is_running():
308328
assert self.process is not None # for mypy
309329
self.process.terminate() # okay to call even if process is already dead
310330
exit_status, stdout, stderr = reap_process(self.process)
311331
self.process = None
312-
return exit_status, stdout.decode(), stderr.decode()
332+
333+
stdout = stdout.decode() if isinstance(stdout, bytes) else stdout
334+
stderr = stderr.decode() if isinstance(stderr, bytes) else stderr
313335

314336
assert self.process is None # means we're not running
315-
return None, "", ""
337+
self._staticx_cleanup()
338+
return exit_status, stdout, stderr
316339

317340
def stop(self) -> None:
318341
exit_status, stdout, stderr = self._terminate()

gprofiler/utils/__init__.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
# Copyright (C) 2022 Intel Corporation
33
#
44
# Licensed under the Apache License, Version 2.0 (the "License");
5-
65
# you may not use this file except in compliance with the License.
76
# You may obtain a copy of the License at
87
#
@@ -99,7 +98,12 @@ def is_root() -> bool:
9998
return os.geteuid() == 0
10099

101100

102-
def start_process(cmd: Union[str, List[str]], via_staticx: bool = False, **kwargs: Any) -> Popen:
101+
def start_process(
102+
cmd: Union[str, List[str]],
103+
via_staticx: bool = False,
104+
tmpdir: Optional[Path] = None,
105+
**kwargs: Any,
106+
) -> Popen:
103107
global _processes
104108

105109
if isinstance(cmd, str):
@@ -122,9 +126,16 @@ def start_process(cmd: Union[str, List[str]], via_staticx: bool = False, **kwarg
122126
# see https://github.com/JonathonReinhart/staticx#run-time-information
123127
cmd = [f"{staticx_dir}/.staticx.interp", "--library-path", staticx_dir] + cmd
124128
else:
125-
# explicitly remove our directory from LD_LIBRARY_PATH
126129
env = env if env is not None else os.environ.copy()
127-
env.update({"LD_LIBRARY_PATH": ""})
130+
if tmpdir is not None:
131+
tmpdir.mkdir(exist_ok=True)
132+
env["TMPDIR"] = tmpdir.as_posix()
133+
elif "TMPDIR" not in env and "TMPDIR" in os.environ:
134+
# ensure `TMPDIR` env is propagated to the child processes (used by staticx)
135+
env["TMPDIR"] = os.environ["TMPDIR"]
136+
137+
# explicitly remove our directory from LD_LIBRARY_PATH
138+
env["LD_LIBRARY_PATH"] = ""
128139

129140
process = Popen(
130141
cmd,

0 commit comments

Comments
 (0)