Skip to content

Commit fa09f89

Browse files
committed
chore: symbolize stack traces in tests upon crash
We disable address space randomization when building the binary and use addr2line to symbolize the stacktrace if it exists. Signed-off-by: Roman Gershman <[email protected]>
1 parent 267bd43 commit fa09f89

File tree

4 files changed

+38
-4
lines changed

4 files changed

+38
-4
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,14 @@ jobs:
100100
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
101101
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
102102
run: |
103+
# -no-pie to disable address randomization so we could symbolize stacktraces
103104
cmake -B ${GITHUB_WORKSPACE}/build \
104105
-DCMAKE_BUILD_TYPE=${{matrix.build-type}} \
105106
-GNinja \
106107
-DCMAKE_C_COMPILER="${{matrix.compiler.c}}" \
107108
-DCMAKE_CXX_COMPILER="${{matrix.compiler.cxx}}" \
108109
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache -DCMAKE_C_COMPILER_LAUNCHER=sccache \
109-
-DCMAKE_CXX_FLAGS="${{matrix.cxx_flags}}" -DWITH_AWS:BOOL=OFF \
110+
-DCMAKE_CXX_FLAGS="${{matrix.cxx_flags}} -no-pie" -DWITH_AWS:BOOL=OFF \
110111
-L
111112
cd ${GITHUB_WORKSPACE}/build && pwd
112113
du -hcs _deps/

.github/workflows/regression-tests.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ jobs:
2929

3030
- name: Configure & Build
3131
run: |
32+
# -no-pie to disable address randomization so we could symbolize stacktraces
3233
cmake -B ${GITHUB_WORKSPACE}/build -DCMAKE_BUILD_TYPE=${{matrix.build-type}} -GNinja \
33-
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DPRINT_STACKTRACES_ON_SIGNAL=ON
34+
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DPRINT_STACKTRACES_ON_SIGNAL=ON \
35+
-DCMAKE_CXX_FLAGS=-no-pie
3436
3537
cd ${GITHUB_WORKSPACE}/build && ninja dragonfly
3638
pwd

tests/dragonfly/connection_test.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,8 @@ async def test_unix_domain_socket(df_factory, tmp_dir):
707707

708708
async def test_unix_socket_only(df_factory, tmp_dir):
709709
server = df_factory.create(proactor_threads=1, port=0, unixsocket="./df.sock")
710+
# we call _start because we start() wait for the port to become available and
711+
# we run here a process without a port.
710712
server._start()
711713

712714
await asyncio.sleep(1)

tests/dragonfly/instance.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,22 @@ class DflyStartException(Exception):
4747
pass
4848

4949

50+
def symbolize_stack_trace(binary_path, lines):
51+
pattern = rb"@\s*(0x[0-9a-fA-F]+)"
52+
matcher = re.compile(pattern)
53+
addr2line_proc = subprocess.Popen(
54+
["/usr/bin/addr2line", "-fCa", "-e", binary_path], stdin=subprocess.PIPE
55+
)
56+
for line in lines:
57+
res = matcher.search(line)
58+
if res:
59+
g = res.group(1) + b"\n"
60+
addr2line_proc.stdin.write(g)
61+
62+
addr2line_proc.stdin.close()
63+
addr2line_proc.wait()
64+
65+
5066
class DflyInstance:
5167
"""
5268
Represents a runnable and stoppable Dragonfly instance
@@ -60,8 +76,9 @@ def __init__(self, params: DflyParams, args):
6076
self.proc: Optional[subprocess.Popen] = None
6177
self._client: Optional[RedisClient] = None
6278
self.log_files: List[str] = []
63-
6479
self.dynamic_port = False
80+
self.sed_proc = None
81+
6582
if self.params.existing_port:
6683
self._port = self.params.existing_port
6784
elif "port" in self.args:
@@ -150,7 +167,7 @@ def _wait_for_server(self):
150167
sed_cmd = ["sed", "-u", "-e", sed_format]
151168
if self.params.buffered_out:
152169
sed_cmd.remove("-u")
153-
subprocess.Popen(sed_cmd, stdin=self.proc.stdout)
170+
self.sed_proc = subprocess.Popen(sed_cmd, stdin=self.proc.stdout, stdout=subprocess.PIPE)
154171

155172
def set_proc_to_none(self):
156173
self.proc = None
@@ -188,6 +205,18 @@ def stop(self, kill=False):
188205
proc.kill()
189206
proc.communicate()
190207
raise Exception("Unable to terminate DragonflyDB gracefully, it was killed")
208+
finally:
209+
if self.sed_proc:
210+
sed_out = self.sed_proc.stdout.readlines()
211+
212+
# Deduplicate output - we somewhere duplicate the output, probably due
213+
# to tty redirections.
214+
seen = set()
215+
sed_out = [x for x in sed_out if not (x in seen or seen.add(x))]
216+
217+
for str in sed_out:
218+
print(str.decode())
219+
symbolize_stack_trace(proc.args[0], sed_out)
191220

192221
def _start(self):
193222
if self.params.existing_port:

0 commit comments

Comments
 (0)