Skip to content
This repository was archived by the owner on Apr 5, 2026. It is now read-only.

Commit df5b32d

Browse files
committed
fix: enable crash stack traces on Alpine, add test healthcheck
Worker SIGSEGV crashes in CI produce "(stack trace unavailable on musl)" making root cause diagnosis impossible. Add libunwind-based backtrace as a fallback on non-glibc systems, gated behind DEBUG_TOOLS=1 build arg so production images stay lean. Also add healthcheck to docker-compose.test.yml so the tester waits for the proxy to be ready instead of using a hardcoded 10s sleep.
1 parent 7ff8124 commit df5b32d

9 files changed

Lines changed: 55 additions & 6 deletions

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ jobs:
6565
- name: Install Dependencies
6666
run: |
6767
sudo apt-get update
68-
sudo apt-get install -y build-essential libssl-dev zlib1g-dev git curl vim-common python3 python3-pip python3-venv netcat-openbsd
68+
sudo apt-get install -y build-essential libssl-dev zlib1g-dev git curl vim-common python3 python3-pip python3-venv netcat-openbsd libunwind-dev
6969
python3 -m venv .venv
7070
source .venv/bin/activate
7171
pip install requests telethon

Dockerfile

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ FROM alpine:3.21 AS builder
33

44
# Install build dependencies
55
# linux-headers: provides <linux/futex.h> used by mp-queue.c and jobs.c
6-
RUN apk add --no-cache build-base openssl-dev zlib-dev linux-headers git
6+
# DEBUG_TOOLS=1: adds libunwind for stack traces in crash dumps (test/CI only)
7+
ARG DEBUG_TOOLS=0
8+
RUN apk add --no-cache build-base openssl-dev zlib-dev linux-headers git \
9+
$([ "$DEBUG_TOOLS" = "1" ] && echo "libunwind-dev")
710

811
# Set working directory
912
WORKDIR /src
@@ -24,7 +27,9 @@ FROM alpine:3.21
2427
# zlib: required by mtproto-proxy (-lz)
2528
# iproute2: ip command for local IP detection in NAT setup
2629
# ca-certificates: TLS certificate verification
27-
RUN apk add --no-cache curl ca-certificates openssl zlib iproute2
30+
ARG DEBUG_TOOLS=0
31+
RUN apk add --no-cache curl ca-certificates openssl zlib iproute2 \
32+
$([ "$DEBUG_TOOLS" = "1" ] && echo "libunwind")
2833

2934
# Create user for running the proxy
3035
RUN adduser -D -H -s /sbin/nologin mtproxy

Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ HOST_ARCH := $(shell arch)
2323
COMMON_CFLAGS := -O3 -std=gnu11 -Wall -fno-strict-aliasing -fno-strict-overflow -fwrapv -DAES=1 -DCOMMIT=\"${COMMIT}\" -DVERSION=\"${VERSION}\" -D_GNU_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wno-array-bounds -Wno-implicit-function-declaration
2424
COMMON_LDFLAGS := -ggdb -rdynamic -lm -lrt -lcrypto -lz -lpthread
2525

26+
# Auto-detect libunwind for stack traces on musl/Alpine (test/CI builds)
27+
LIBUNWIND_CFLAGS := $(shell pkg-config --cflags libunwind 2>/dev/null)
28+
LIBUNWIND_LDFLAGS := $(shell pkg-config --libs libunwind 2>/dev/null)
29+
ifneq ($(LIBUNWIND_LDFLAGS),)
30+
COMMON_CFLAGS += -DHAVE_LIBUNWIND $(LIBUNWIND_CFLAGS)
31+
COMMON_LDFLAGS += $(LIBUNWIND_LDFLAGS)
32+
endif
33+
2634
# Architecture-specific CFLAGS
2735
ifeq ($(HOST_ARCH), x86_64)
2836
CFLAGS := $(COMMON_CFLAGS) -mpclmul -march=core2 -mfpmath=sse -mssse3 $(BITNESS_FLAGS)

common/server-functions.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737
#include <errno.h>
3838
#ifdef __GLIBC__
3939
#include <execinfo.h>
40+
#elif defined(HAVE_LIBUNWIND)
41+
#define UNW_LOCAL_ONLY
42+
#include <libunwind.h>
4043
#endif
4144
#include <fcntl.h>
4245
#include <getopt.h>
@@ -176,6 +179,24 @@ void print_backtrace (void) {
176179
kwrite (2, "\n------- Stack Backtrace -------\n", 33);
177180
backtrace_symbols_fd (buffer, nptrs, 2);
178181
kwrite (2, "-------------------------------\n", 32);
182+
#elif defined(HAVE_LIBUNWIND)
183+
kwrite (2, "\n------- Stack Backtrace -------\n", 33);
184+
unw_cursor_t cursor;
185+
unw_context_t context;
186+
unw_getcontext (&context);
187+
unw_init_local (&cursor, &context);
188+
while (unw_step (&cursor) > 0) {
189+
unw_word_t offset, pc;
190+
char fname[128];
191+
unw_get_reg (&cursor, UNW_REG_IP, &pc);
192+
fname[0] = '\0';
193+
unw_get_proc_name (&cursor, fname, sizeof (fname), &offset);
194+
char line[256];
195+
int len = snprintf (line, sizeof (line), " %s (+0x%lx) [0x%lx]\n",
196+
fname[0] ? fname : "???", (long)offset, (long)pc);
197+
if (len > 0) kwrite (2, line, len);
198+
}
199+
kwrite (2, "-------------------------------\n", 32);
179200
#else
180201
kwrite (2, "\n(stack trace unavailable on musl)\n", 35);
181202
#endif

tests/docker-compose.ip-acl-test.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ services:
33
build:
44
context: ..
55
dockerfile: Dockerfile
6+
args:
7+
DEBUG_TOOLS: "1"
68
environment:
79
- SECRET=${MTPROXY_SECRET}
810
- PORT=8443

tests/docker-compose.multi-secret-test.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ services:
1515
build:
1616
context: ..
1717
dockerfile: Dockerfile
18+
args:
19+
DEBUG_TOOLS: "1"
1820
environment:
1921
- SECRET=${MTPROXY_SECRET_1},${MTPROXY_SECRET_2}
2022
- PORT=8443

tests/docker-compose.test.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ services:
33
build:
44
context: ..
55
dockerfile: Dockerfile
6+
args:
7+
DEBUG_TOOLS: "1"
68
ports:
79
- "18443:8443"
810
- "18888:8888"
@@ -14,11 +16,18 @@ services:
1416
- SECRET=${MTPROXY_SECRET}
1517
- PORT=8443
1618
- WORKERS=1
19+
healthcheck:
20+
test: ["CMD-SHELL", "curl -sf http://localhost:8888/stats || exit 1"]
21+
interval: 2s
22+
timeout: 5s
23+
retries: 30
24+
start_period: 5s
1725
tester:
1826
build: .
1927
environment:
2028
- MTPROXY_HOST=mtproxy
2129
- MTPROXY_PORT=8443
2230
- MTPROXY_STATS_PORT=8888
2331
depends_on:
24-
- mtproxy
32+
mtproxy:
33+
condition: service_healthy

tests/docker-compose.tls-test.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ services:
1515
build:
1616
context: ..
1717
dockerfile: Dockerfile
18+
args:
19+
DEBUG_TOOLS: "1"
1820
environment:
1921
- SECRET=${MTPROXY_SECRET:-d41d8cd98f00b204e9800998ecf8427e}
2022
- PORT=8443

tests/test_proxy.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ def check_upstream_connectivity():
136136
# Check upstream connectivity first (informational)
137137
check_upstream_connectivity()
138138

139-
# Give the proxy time to start and establish ME relay connections
140-
time.sleep(10)
139+
# Small buffer — docker-compose healthcheck ensures the proxy is ready
140+
time.sleep(2)
141141

142142
stats_ok = test_http_stats()
143143
metrics_ok = test_prometheus_metrics()

0 commit comments

Comments
 (0)