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
17 changes: 17 additions & 0 deletions gprofiler/profilers/java.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,20 @@ def __str__(self) -> str:
)


class JattachSocketMissingException(JattachExceptionBase):
def __str__(self) -> str:
# the attach listener is initialized once, then it is marked as initialized:
# (https://github.com/openjdk/jdk/blob/3d07b3c7f01b60ff4dc38f62407c212b48883dbf/src/hotspot/share/services/attachListener.cpp#L388)
# and will not be initialized again:
# https://github.com/openjdk/jdk/blob/3d07b3c7f01b60ff4dc38f62407c212b48883dbf/src/hotspot/os/linux/attachListener_linux.cpp#L509
# since openjdk 2870c9d55efe, the attach socket will be recreated even when removed (and this exception
# won't happen).
return super().__str__() + (
"\nJVM attach socket is missing and jattach could not create it. It has most"
" likely been removed; the process has to be restarted for a new socket to be created."
)


_JAVA_VERSION_TIMEOUT = 5


Expand Down Expand Up @@ -443,6 +457,9 @@ def _run_async_profiler(self, cmd: List[str]) -> None:
args = e.returncode, e.cmd, e.stdout, e.stderr, self.process.pid, ap_log, is_loaded
if isinstance(e, CalledProcessTimeoutError):
raise JattachTimeout(*args, timeout=self._jattach_timeout) from None
elif e.stderr == b"Could not start attach mechanism: No such file or directory\n":
# this is true for jattach_hotspot
raise JattachSocketMissingException(*args) from None
else:
raise JattachException(*args) from None

Expand Down
24 changes: 24 additions & 0 deletions tests/test_java.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import pytest
from docker.models.containers import Container
from granulate_utils.linux.elf import get_elf_buildid
from granulate_utils.linux.ns import get_process_nspid
from packaging.version import Version
from pytest import LogCaptureFixture, MonkeyPatch

Expand Down Expand Up @@ -507,3 +508,26 @@ def start_async_profiler_and_interrupt(self: AsyncProfiledProcess, *args: Any, *
assert profile.appid == "java: Fibonacci.jar"
# and application metadata for java
assert profile.app_metadata is not None and "java_version" in profile.app_metadata


@pytest.mark.parametrize("in_container", [True]) # only in container is enough
def test_java_attach_socket_missing(
tmp_path: Path,
application_pid: int,
) -> None:
"""
Tests that we get the proper JattachMissingSocketException when the attach socket is deleted.
"""

with make_java_profiler(
storage_dir=str(tmp_path),
duration=1,
) as profiler:
snapshot_one_profile(profiler)

# now the attach socket is created, remove it
Path(f"/proc/{application_pid}/root/tmp/.java_pid{get_process_nspid(application_pid)}").unlink()

profile = snapshot_one_profile(profiler)
assert len(profile.stacks) == 1
assert next(iter(profile.stacks.keys())) == "java;[Profiling error: exception JattachSocketMissingException]"