Skip to content

Commit

Permalink
Add CI memory leak tests for liveliness
Browse files Browse the repository at this point in the history
  • Loading branch information
sashacmc committed Nov 22, 2024
1 parent d33f058 commit 5586e0b
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 32 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ jobs:
- name: Build project and run test
run: |
sudo apt install -y ninja-build
CMAKE_GENERATOR=Ninja make
Z_FEATURE_UNSTABLE_API=1 Z_FEATURE_LIVELINESS=1 CMAKE_GENERATOR=Ninja make
python3 ./build/tests/memory_leak.py
timeout-minutes: 5

Expand Down
3 changes: 2 additions & 1 deletion examples/unix/c11/z_get_liveliness.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,10 @@ int main(int argc, char **argv) {
} else {
printf("Received an error\n");
}
z_drop(z_move(reply));
}

z_drop(z_move(reply));
z_drop(z_move(closure));
z_drop(z_move(handler));
z_drop(z_move(s));
return 0;
Expand Down
12 changes: 11 additions & 1 deletion examples/unix/c11/z_sub_liveliness.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

#if Z_FEATURE_SUBSCRIPTION == 1 && Z_FEATURE_LIVELINESS == 1

static volatile int msg_nb = 0;

void data_handler(z_loaned_sample_t *sample, void *ctx) {
(void)(ctx);
z_view_string_t key_string;
Expand All @@ -35,6 +37,7 @@ void data_handler(z_loaned_sample_t *sample, void *ctx) {
z_string_data(z_loan(key_string)));
break;
}
msg_nb++;
}

int main(int argc, char **argv) {
Expand All @@ -43,6 +46,7 @@ int main(int argc, char **argv) {
char *clocator = NULL;
char *llocator = NULL;
bool history = false;
int n = 0;

int opt;
while ((opt = getopt(argc, argv, "k:e:m:l:n:h")) != -1) {
Expand All @@ -62,8 +66,11 @@ int main(int argc, char **argv) {
case 'h':
history = true;
break;
case 'n':
n = atoi(optarg);
break;
case '?':
if (optopt == 'k' || optopt == 'e' || optopt == 'm' || optopt == 'l') {
if (optopt == 'k' || optopt == 'e' || optopt == 'm' || optopt == 'l' || optopt == 'n') {
fprintf(stderr, "Option -%c requires an argument.\n", optopt);
} else {
fprintf(stderr, "Unknown option `-%c'.\n", optopt);
Expand Down Expand Up @@ -116,6 +123,9 @@ int main(int argc, char **argv) {

printf("Press CTRL-C to quit...\n");
while (1) {
if (n != 0 && msg_nb >= n) {
break;
}
z_sleep_s(1);
}

Expand Down
67 changes: 38 additions & 29 deletions tests/memory_leak.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import argparse
import os
from signal import SIGINT
import signal
import subprocess
import sys
import time
import re

# Specify the directory for the binaries
DIR_EXAMPLES = "build/examples"

NO_LEAK_OUTPUT = "All heap blocks were freed -- no leaks are possible"

VALGRIND_CMD = f"stdbuf -oL -eL valgrind --leak-check=full ./{DIR_EXAMPLES}/"


def failure_mode(fail_cmd):
test_status = 0
# Start binary
print("Start binary")
z_pub_command = f"stdbuf -oL -eL valgrind ./{DIR_EXAMPLES}/" + fail_cmd
z_pub_command = VALGRIND_CMD + fail_cmd
z_pub_process = subprocess.Popen(
z_pub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
)
Expand All @@ -38,31 +38,31 @@ def failure_mode(fail_cmd):
def pub_and_sub(pub_cmd, sub_cmd):
test_status = 0

print("Start subscriber")
print(f"Start {sub_cmd}")
# Start z_sub in the background
z_sub_command = f"stdbuf -oL -eL valgrind ./{DIR_EXAMPLES}/" + sub_cmd
z_sub_command = VALGRIND_CMD + sub_cmd

z_sub_process = subprocess.Popen(
z_sub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
)
# Introduce a delay to ensure z_sub starts
time.sleep(2)

print("Start publisher")
print(f"Start {pub_cmd}")
# Start z_pub
z_pub_command = f"stdbuf -oL -eL valgrind ./{DIR_EXAMPLES}/" + pub_cmd
z_pub_command = VALGRIND_CMD + pub_cmd
z_pub_process = subprocess.Popen(
z_pub_command, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
)
# Wait for z_pub to finish
z_pub_process.wait()

print("Stop subscriber")
print(f"Stop {sub_cmd}")
time.sleep(2)
if z_sub_process.poll() is None:
# send SIGINT to group
z_sub_process_gid = os.getpgid(z_sub_process.pid)
os.killpg(z_sub_process_gid, SIGINT)
os.killpg(z_sub_process_gid, signal.SIGINT)

# Wait for z_sub to finish
z_sub_process.wait()
Expand All @@ -71,18 +71,18 @@ def pub_and_sub(pub_cmd, sub_cmd):
# Check output of z_pub
z_pub_output = z_pub_process.stderr.read()
if NO_LEAK_OUTPUT in z_pub_output:
print("z_pub output valid")
print(f"{pub_cmd} output valid")
else:
print("z_pub output invalid:")
print(f"{pub_cmd} output invalid:")
print(f"Received: \"{z_pub_output}\"")
test_status = 1

# Check output of z_sub
z_sub_output = z_sub_process.stderr.read()
if NO_LEAK_OUTPUT in z_sub_output:
print("z_sub output valid")
print(f"{sub_cmd} output valid")
else:
print("z_sub output invalid:")
print(f"{sub_cmd} output invalid:")
print(f"Received: \"{z_sub_output}\"")
test_status = 1
# Return value
Expand All @@ -91,9 +91,9 @@ def pub_and_sub(pub_cmd, sub_cmd):

def query_and_queryable(query_cmd, queryable_cmd):
test_status = 0
print("Start queryable")
print(f"Start {queryable_cmd}")
# Start z_queryable in the background
z_queryable_command = f"stdbuf -oL -eL valgrind ./{DIR_EXAMPLES}/" + queryable_cmd
z_queryable_command = VALGRIND_CMD + queryable_cmd
z_queryable_process = subprocess.Popen(
z_queryable_command,
shell=True,
Expand All @@ -106,9 +106,9 @@ def query_and_queryable(query_cmd, queryable_cmd):
# Introduce a delay to ensure z_queryable starts
time.sleep(2)

print("Start query")
print(f"Start {query_cmd}")
# Start z_query
z_query_command = f"stdbuf -oL -eL valgrind ./{DIR_EXAMPLES}/" + query_cmd
z_query_command = VALGRIND_CMD + query_cmd
z_query_process = subprocess.Popen(
z_query_command,
shell=True,
Expand All @@ -120,12 +120,12 @@ def query_and_queryable(query_cmd, queryable_cmd):
# Wait for z_query to finish
z_query_process.wait()

print("Stop queryable")
print(f"Stop {queryable_cmd}")
time.sleep(2)
if z_queryable_process.poll() is None:
# send SIGINT to group
z_quaryable_process_gid = os.getpgid(z_queryable_process.pid)
os.killpg(z_quaryable_process_gid, SIGINT)
os.killpg(z_quaryable_process_gid, signal.SIGINT)

# Wait for z_queryable to finish
z_queryable_process.wait()
Expand All @@ -134,44 +134,53 @@ def query_and_queryable(query_cmd, queryable_cmd):
# Check output of z_query
z_query_output = z_query_process.stderr.read()
if NO_LEAK_OUTPUT in z_query_output:
print("z_query output valid")
print(f"{query_cmd} output valid")
else:
print("z_query output invalid:")
print(f"{query_cmd} output invalid:")
print(f'Received: "{z_query_output}"')
test_status = 1

# Check output of z_queryable
z_queryable_output = z_queryable_process.stderr.read()
if NO_LEAK_OUTPUT in z_queryable_output:
print("z_queryable output valid")
print(f"{queryable_cmd} output valid")
else:
print("z_queryable output invalid:")
print(f"{queryable_cmd} output invalid:")
print(f'Received: "{z_queryable_output}"')
test_status = 1
# Return status
return test_status


if __name__ == "__main__":
signal.signal(signal.SIGINT, signal.SIG_IGN)
EXIT_STATUS = 0

# Test failure mode
print("*** Failure mode ***")
if failure_mode(f'z_pub -m peer') == 1:
if failure_mode('z_pub -m peer') == 1:
EXIT_STATUS = 1
# Test pub and sub examples
print("*** Pub & sub test ***")
if pub_and_sub(f'z_pub -n 1', f'z_sub -n 1') == 1:
if pub_and_sub('z_pub -n 1', 'z_sub -n 1') == 1:
EXIT_STATUS = 1
print("*** Pub & sub attachment test ***")
if pub_and_sub(f'z_pub_attachment -n 1', f'z_sub_attachment -n 1') == 1:
if pub_and_sub('z_pub_attachment -n 1', 'z_sub_attachment -n 1') == 1:
EXIT_STATUS = 1
# Test query and queryable examples
print("*** Query & queryable test ***")
if query_and_queryable(f'z_get', f'z_queryable -n 1') == 1:
if query_and_queryable('z_get', 'z_queryable -n 1') == 1:
EXIT_STATUS = 1
print("*** Query & queryable attachment test ***")
if query_and_queryable(f'z_get_attachment -v Something', f'z_queryable_attachment -n 1') == 1:
if query_and_queryable('z_get_attachment -v Something', 'z_queryable_attachment -n 1') == 1:
EXIT_STATUS = 1
# Test liveliness query
print("*** Get liveliness test ***")
if query_and_queryable('z_get_liveliness', 'z_liveliness') == 1:
EXIT_STATUS = 1
# Test liveliness subscriber
print("*** Liveliness subscriber test ***")
if query_and_queryable('z_sub_liveliness -h -n 1', 'z_liveliness') == 1:
EXIT_STATUS = 1
# Exit
sys.exit(EXIT_STATUS)

0 comments on commit 5586e0b

Please sign in to comment.