Skip to content

Commit

Permalink
tests: try to make s2n_mem_usage_test more useful
Browse files Browse the repository at this point in the history
  • Loading branch information
lrstewart committed Feb 20, 2025
1 parent 493248b commit ebd59f3
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 101 deletions.
18 changes: 18 additions & 0 deletions codebuild/spec/buildspec_generalbatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,21 @@ batch:
privileged-mode: true
compute-type: BUILD_GENERAL1_LARGE
image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu22codebuild
- identifier: s2nMemUsageTest_awslc
buildspec: codebuild/spec/buildspec_mem.yml
env:
compute-type: BUILD_GENERAL1_SMALL
image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu24
privileged-mode: true
variables:
S2N_LIBCRYPTO: 'awslc'
S2N_EXPECTED_CONNECTION_MEMORY_KB: 49
- identifier: s2nMemUsageTest_awslcfips
buildspec: codebuild/spec/buildspec_mem.yml
env:
compute-type: BUILD_GENERAL1_SMALL
image: 024603541914.dkr.ecr.us-west-2.amazonaws.com/docker:ubuntu24
privileged-mode: true
variables:
S2N_LIBCRYPTO: 'awslc-fips-2022'
S2N_EXPECTED_CONNECTION_MEMORY_KB: 52
45 changes: 45 additions & 0 deletions codebuild/spec/buildspec_mem.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You may not use
# this file except in compliance with the License. A copy of the License is
# located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied. See the License for the specific language governing permissions and
# limitations under the License.
version: 0.2

env:
shell: bash
variables:
CTEST_OUTPUT_ON_FAILURE: 1
S2N_TEST_NAME: s2n_mem_usage_test

phases:
pre_build:
commands:
- |
if [ -d "third-party-src" ]; then
cd third-party-src;
fi
build:
on-failure: ABORT
commands:
- export CTEST_PARALLEL_LEVEL=$(nproc)
# Test for expected memory
- |
cmake . -Bbuild \
-DCMAKE_PREFIX_PATH=/usr/local/$S2N_LIBCRYPTO
- cmake --build build -j $(nproc) --target $S2N_TEST_NAME
- echo ">>>> POSITIVE TEST: EXPECTED TO SUCCEED <<<<"
- make -C build test -- ARGS="-R $S2N_TEST_NAME"
# Test for unexpected memory to confirm failure possible
# Use an unrealistically high number
- cmake --build build -j $(nproc) --target $S2N_TEST_NAME
- echo ">>>> NEGATIVE TEST: EXPECTED TO FAIL <<<<"
- |
! S2N_EXPECTED_CONNECTION_MEMORY_KB=500 make -C build test -- ARGS="-R $S2N_TEST_NAME"
131 changes: 30 additions & 101 deletions tests/unit/s2n_mem_usage_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,32 +50,11 @@
* usage. The greater the value, the more accurate the end result. */
#define MAX_CONNECTIONS 1000

/* This is roughly the current memory usage per connection, in KB */
#ifdef __FreeBSD__
#define MEM_PER_CONNECTION 57
#elif defined(__OpenBSD__)
#define MEM_PER_CONNECTION 61
#else
#define MEM_PER_CONNECTION 51
#endif

/* This is the maximum memory per connection including 4KB of slack */
#define TEST_SLACK 4
#define MAX_MEM_PER_CONNECTION \
((MEM_PER_CONNECTION + TEST_SLACK) * 1024)

/* This is the total maximum memory allowed */
#define MAX_MEM_ALLOWED(num_connections) \
(2 * (num_connections) *MAX_MEM_PER_CONNECTION)

/* This is the correct value of MEM_PER_CONNECTION based on test results.
* Basically, this calculation should reverse MAX_MEM_ALLOWED */
#define ACTUAL_MEM_PER_CONNECTION(num_connections, max_mem) \
((((max_mem) / 2 / (num_connections)) / 1024) - TEST_SLACK)
/* A change of more than 5% is significant */
#define ALLOWED_VARIANCE .05

ssize_t get_vm_data_size()
{
#ifdef __linux__
long page_size = 0;
ssize_t size = 0, resident = 0, share = 0, text = 0, lib = 0, data = 0, dt = 0;

Expand All @@ -92,57 +71,6 @@ ssize_t get_vm_data_size()
fclose(status_file);

return data * page_size;

#elif defined(__FreeBSD__)
pid_t ppid = getpid();
int pidinfo[4];
pidinfo[0] = CTL_KERN;
pidinfo[1] = KERN_PROC;
pidinfo[2] = KERN_PROC_PID;
pidinfo[3] = (int) ppid;

struct kinfo_proc procinfo = { 0 };

size_t len = sizeof(procinfo);

sysctl(pidinfo, nitems(pidinfo), &procinfo, &len, NULL, 0);

/* Taken from linprocfs implementation
* https://github.com/freebsd/freebsd-src/blob/779fd05344662aeec79c29470258bf657318eab3/sys/compat/linprocfs/linprocfs.c#L1019 */
segsz_t lsize = (procinfo.ki_size >> PAGE_SHIFT) - procinfo.ki_dsize - procinfo.ki_ssize - procinfo.ki_tsize - 1;

return lsize << PAGE_SHIFT;

#elif defined(__OpenBSD__)
struct kinfo_proc *procinfo;
kvm_t *kd;
pid_t ppid;
long page_size;
ssize_t size;
int nentries;

kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, NULL);
ppid = getpid();
procinfo = kvm_getprocs(kd, KERN_PROC_PID, ppid, sizeof(*procinfo), &nentries);
if (procinfo == NULL || nentries == 0) {
return -1;
}

/* Taken from ps(1)'s calculation of vsize
* https://github.com/openbsd/src/blob/329e3480337617df4d195c9a400c3f186254b137/bin/ps/print.c#L603 */
size = procinfo->p_vm_dsize + procinfo->p_vm_ssize + procinfo->p_vm_tsize;

page_size = sysconf(_SC_PAGESIZE);
if (page_size < 0) {
return -1;
}
kvm_close(kd);

return (size * page_size);
#else
/* Not implemented for other platforms */
return 0;
#endif
}

int main(int argc, char **argv)
Expand All @@ -158,11 +86,18 @@ int main(int argc, char **argv)
DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close);
EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));

/* Skip the test when running under valgrind or address sanitizer, as those tools
* impact the memory usage. */
if (getenv("S2N_VALGRIND") != NULL || getenv("S2N_ADDRESS_SANITIZER") != NULL) {
/* Skip the test unless specifically enabled.
* This test is too unreliable to run in all customer environments.
* We should choose specific, known builds to run this test in.
*/
const char *env_var = getenv("S2N_EXPECTED_CONNECTION_MEMORY_KB");
if (env_var == NULL) {
END_TEST();
}
const int expected_kbs_per_conn = atoi(env_var);
EXPECT_TRUE(expected_kbs_per_conn > 1);
const int allowed_diff = expected_kbs_per_conn * ALLOWED_VARIANCE;
EXPECT_TRUE(allowed_diff > 0);

struct rlimit file_limit;
EXPECT_SUCCESS(getrlimit(RLIMIT_NOFILE, &file_limit));
Expand All @@ -172,9 +107,6 @@ int main(int argc, char **argv)
connectionsToUse = MAX(1, (file_limit.rlim_cur - 16) / 4);
}

const ssize_t maxAllowedMemDiff = MAX_MEM_ALLOWED(connectionsToUse);
const ssize_t minAllowedMemDiff = maxAllowedMemDiff * 0.75;

struct s2n_connection **clients = calloc(connectionsToUse, sizeof(struct s2n_connection *));
struct s2n_connection **servers = calloc(connectionsToUse, sizeof(struct s2n_connection *));

Expand Down Expand Up @@ -253,34 +185,31 @@ int main(int argc, char **argv)
free(clients);
free(servers);

TEST_DEBUG_PRINT("\n");
TEST_DEBUG_PRINT("VmData initial: %10zd\n", vm_data_initial);
TEST_DEBUG_PRINT("VmData after allocations: %10zd\n", vm_data_after_allocation);
TEST_DEBUG_PRINT("VmData after handshakes: %10zd\n", vm_data_after_handshakes);
TEST_DEBUG_PRINT("VmData after free handshake: %10zd\n", vm_data_after_free_handshake);
TEST_DEBUG_PRINT("VmData after release: %10zd\n", vm_data_after_release_buffers);
TEST_DEBUG_PRINT("Max VmData diff allowed: %10zd\n", maxAllowedMemDiff);
TEST_DEBUG_PRINT("Number of connections used: %10zu\n", connectionsToUse);

EXPECT_TRUE(vm_data_after_free_handshake <= vm_data_after_handshakes);
EXPECT_TRUE(vm_data_after_release_buffers <= vm_data_after_free_handshake);

ssize_t handshake_diff = (vm_data_after_handshakes - vm_data_initial);
ssize_t allocation_diff = (vm_data_after_allocation - vm_data_initial);

/*
* get_vm_data_size is required for this test to succeed.
* Any platform that doesn't implement get_vm_data_size should be excluded here.
*/
#ifndef __APPLE__
if (allocation_diff > maxAllowedMemDiff
|| handshake_diff > maxAllowedMemDiff
|| handshake_diff < minAllowedMemDiff) {
fprintf(stdout, "\nActual KB per connection: %i\n",
(int) ACTUAL_MEM_PER_CONNECTION(connectionsToUse, handshake_diff));
EXPECT_TRUE(allocation_diff <= handshake_diff);

ssize_t mem_per_conn = handshake_diff / (connectionsToUse * 2);
ssize_t kbs_per_conn = mem_per_conn / 1024;

if (kbs_per_conn < expected_kbs_per_conn - allowed_diff
|| kbs_per_conn > expected_kbs_per_conn + allowed_diff) {
printf("\nActual KB per connection: %li\n", kbs_per_conn);
printf("This is a %.2f%% change\n",
(kbs_per_conn - expected_kbs_per_conn) * 100.0 / expected_kbs_per_conn);

printf("\n");
printf("VmData initial: %10zd\n", vm_data_initial);
printf("VmData after allocations: %10zd\n", vm_data_after_allocation);
printf("VmData after handshakes: %10zd\n", vm_data_after_handshakes);
printf("VmData after free handshake: %10zd\n", vm_data_after_free_handshake);
printf("VmData after release: %10zd\n", vm_data_after_release_buffers);
printf("Number of connections used: %10zu\n", connectionsToUse);
FAIL_MSG("Unexpected memory usage. If expected, update MEM_PER_CONNECTION.");
}
#endif

END_TEST();
}

0 comments on commit ebd59f3

Please sign in to comment.