Compare commits
28 Commits
Author | SHA1 | Date |
---|---|---|
|
c7276a354a | |
|
351acf09eb | |
|
27f484fcc7 | |
|
79bb161cd2 | |
|
6be9def07e | |
|
6d05cc5e8d | |
|
f4670cffea | |
|
4c9dcc0a0e | |
|
e1a2db6b78 | |
|
e4d4263a3d | |
|
9c6b325e84 | |
|
c8985e2289 | |
|
ec11f29c1c | |
|
b7e51eb669 | |
|
105659ad1b | |
|
d477a05906 | |
|
a9e4072d67 | |
|
9a5b7dc5fe | |
|
9248c1695e | |
|
ceadfcc7cd | |
|
32aa75e9f3 | |
|
d921af231d | |
|
9a6bd05498 | |
|
18dc06410b | |
|
e1765a9e89 | |
|
adbdec31b8 | |
|
019b361959 | |
|
f7a5a50a91 |
|
@ -293,6 +293,10 @@ jobs:
|
||||||
git config --global --add safe.directory $GITHUB_WORKSPACE
|
git config --global --add safe.directory $GITHUB_WORKSPACE
|
||||||
|
|
||||||
- name: Build and test 🏗️🧪
|
- name: Build and test 🏗️🧪
|
||||||
|
env:
|
||||||
|
# This avoids random failures on CI.
|
||||||
|
# (https://github.com/google/sanitizers/issues/1322#issuecomment-699946942)
|
||||||
|
ASAN_OPTIONS: intercept_tls_get_addr=0
|
||||||
run: |
|
run: |
|
||||||
mkdir -p build && cd build
|
mkdir -p build && cd build
|
||||||
cmake -DBUILD_BPF=ON \
|
cmake -DBUILD_BPF=ON \
|
||||||
|
@ -302,8 +306,8 @@ jobs:
|
||||||
-DUSE_BUNDLED_LIBBPF=ON \
|
-DUSE_BUNDLED_LIBBPF=ON \
|
||||||
..
|
..
|
||||||
make -j$(nproc) sinsp-example driver bpf
|
make -j$(nproc) sinsp-example driver bpf
|
||||||
sudo make e2e-install-deps
|
sudo -E make e2e-install-deps
|
||||||
sudo ../test/e2e/scripts/run_tests.sh
|
sudo -E ../test/e2e/scripts/run_tests.sh
|
||||||
|
|
||||||
- name: Archive test reports
|
- name: Archive test reports
|
||||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||||
|
|
|
@ -74,7 +74,7 @@ jobs:
|
||||||
sudo apt install -y --no-install-recommends linux-headers-$(uname -r) gcc-multilib g++-multilib
|
sudo apt install -y --no-install-recommends linux-headers-$(uname -r) gcc-multilib g++-multilib
|
||||||
|
|
||||||
- name: Run sccache-cache
|
- name: Run sccache-cache
|
||||||
uses: mozilla-actions/sccache-action@v0.0.3
|
uses: mozilla-actions/sccache-action@v0.0.4
|
||||||
|
|
||||||
- name: Build e2e tests 🏗️
|
- name: Build e2e tests 🏗️
|
||||||
env:
|
env:
|
||||||
|
@ -96,10 +96,11 @@ jobs:
|
||||||
-DUSE_BUNDLED_GTEST=ON \
|
-DUSE_BUNDLED_GTEST=ON \
|
||||||
..
|
..
|
||||||
make -j6 libsinsp_e2e_tests
|
make -j6 libsinsp_e2e_tests
|
||||||
|
sudo rm -vfr test/libsinsp_e2e/resources/_proc
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
- name: Cache build
|
- name: Cache build
|
||||||
uses: actions/cache/save@v3
|
uses: actions/cache/save@v4.0.2
|
||||||
if: always()
|
if: always()
|
||||||
id: cache
|
id: cache
|
||||||
with:
|
with:
|
||||||
|
@ -123,7 +124,7 @@ jobs:
|
||||||
|
|
||||||
- name: Restore build
|
- name: Restore build
|
||||||
id: cache
|
id: cache
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v4.0.2
|
||||||
with:
|
with:
|
||||||
path: build
|
path: build
|
||||||
key: build-e2e-${{ matrix.arch }}-${{ github.run_id }}
|
key: build-e2e-${{ matrix.arch }}-${{ github.run_id }}
|
||||||
|
@ -146,7 +147,7 @@ jobs:
|
||||||
|
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: |
|
run: |
|
||||||
sudo apt install -y --no-install-recommends clang gcc llvm build-essential cmake
|
sudo apt install -y --no-install-recommends clang gcc llvm build-essential cmake python2
|
||||||
|
|
||||||
- name: Install kernel headers (actuated)
|
- name: Install kernel headers (actuated)
|
||||||
uses: self-actuated/get-kernel-sources@201eed7d915ac0a6021fb402cde5be7a6b945b59
|
uses: self-actuated/get-kernel-sources@201eed7d915ac0a6021fb402cde5be7a6b945b59
|
||||||
|
@ -161,9 +162,12 @@ jobs:
|
||||||
# different workers, so we rebuild the drivers.
|
# different workers, so we rebuild the drivers.
|
||||||
- name: Rebuild drivers
|
- name: Rebuild drivers
|
||||||
run: |
|
run: |
|
||||||
cd build
|
pushd build
|
||||||
make -B driver bpf
|
make -B driver bpf
|
||||||
cd ..
|
pushd test/libsinsp_e2e/resources/
|
||||||
|
sudo tar xzf fake-proc.tar.gz
|
||||||
|
popd
|
||||||
|
popd
|
||||||
|
|
||||||
- name: Run e2e tests with ${{ matrix.driver.name }} 🏎️
|
- name: Run e2e tests with ${{ matrix.driver.name }} 🏎️
|
||||||
if: matrix.arch == 'amd64'
|
if: matrix.arch == 'amd64'
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
2.20.0
|
2.20.1
|
||||||
|
|
|
@ -33,12 +33,7 @@ or GPL2.txt for full copies of the license.
|
||||||
* structures. But the syscalls (might) still use them.
|
* structures. But the syscalls (might) still use them.
|
||||||
*/
|
*/
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
|
||||||
#include <linux/time64.h>
|
#include <linux/time32.h>
|
||||||
struct compat_timespec {
|
|
||||||
int32_t tv_sec;
|
|
||||||
int32_t tv_nsec;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct timespec {
|
struct timespec {
|
||||||
int32_t tv_sec;
|
int32_t tv_sec;
|
||||||
int32_t tv_nsec;
|
int32_t tv_nsec;
|
||||||
|
@ -48,8 +43,15 @@ struct timeval {
|
||||||
int32_t tv_sec;
|
int32_t tv_sec;
|
||||||
int32_t tv_usec;
|
int32_t tv_usec;
|
||||||
};
|
};
|
||||||
|
typedef struct old_timespec32 old_timespec32;
|
||||||
#else
|
#else
|
||||||
|
#if __has_include(<linux/time32.h>)
|
||||||
|
#include <linux/time32.h>
|
||||||
|
#else
|
||||||
|
#include <linux/compat.h>
|
||||||
|
#endif
|
||||||
#define timeval64 timeval
|
#define timeval64 timeval
|
||||||
|
typedef struct compat_timespec old_timespec32;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define FILLER_RAW(x) \
|
#define FILLER_RAW(x) \
|
||||||
|
@ -469,7 +471,7 @@ FILLER(sys_write_x, true)
|
||||||
static __always_inline int bpf_poll_parse_fds(struct filler_data *data,
|
static __always_inline int bpf_poll_parse_fds(struct filler_data *data,
|
||||||
bool enter_event)
|
bool enter_event)
|
||||||
{
|
{
|
||||||
unsigned int read_size;
|
unsigned long read_size;
|
||||||
unsigned int fds_count;
|
unsigned int fds_count;
|
||||||
int res = PPM_SUCCESS;
|
int res = PPM_SUCCESS;
|
||||||
unsigned long nfds;
|
unsigned long nfds;
|
||||||
|
@ -570,17 +572,18 @@ FILLER(sys_poll_x, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAX_IOVCNT 32
|
#define MAX_IOVCNT 32
|
||||||
|
#define MAX_IOVCNT_COMPAT 8
|
||||||
|
|
||||||
static __always_inline int bpf_parse_readv_writev_bufs(struct filler_data *data,
|
static __always_inline int bpf_parse_readv_writev_bufs_64(struct filler_data *data,
|
||||||
const struct iovec __user *iovsrc,
|
const void __user *iovsrc,
|
||||||
unsigned long iovcnt,
|
unsigned long iovcnt,
|
||||||
long retval,
|
long retval,
|
||||||
int flags)
|
int flags,
|
||||||
|
unsigned long *size)
|
||||||
{
|
{
|
||||||
const struct iovec *iov;
|
const struct iovec *iov;
|
||||||
int res = PPM_SUCCESS;
|
int res = PPM_SUCCESS;
|
||||||
unsigned int copylen;
|
unsigned long copylen;
|
||||||
long size = 0;
|
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
copylen = iovcnt * sizeof(struct iovec);
|
copylen = iovcnt * sizeof(struct iovec);
|
||||||
|
@ -603,31 +606,30 @@ static __always_inline int bpf_parse_readv_writev_bufs(struct filler_data *data,
|
||||||
#endif
|
#endif
|
||||||
return PPM_FAILURE_INVALID_USER_MEMORY;
|
return PPM_FAILURE_INVALID_USER_MEMORY;
|
||||||
|
|
||||||
|
|
||||||
#pragma unroll
|
#pragma unroll
|
||||||
for (j = 0; j < MAX_IOVCNT; ++j) {
|
for (j = 0; j < MAX_IOVCNT; ++j) {
|
||||||
if (j == iovcnt)
|
if (j == iovcnt)
|
||||||
break;
|
break;
|
||||||
// BPF seems to require a hard limit to avoid overflows
|
// BPF seems to require a hard limit to avoid overflows
|
||||||
if (size == LONG_MAX)
|
if (*size == LONG_MAX)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
size += iov[j].iov_len;
|
*size += iov[j].iov_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & PRB_FLAG_IS_WRITE) == 0)
|
if ((flags & PRB_FLAG_IS_WRITE) == 0)
|
||||||
if (size > retval)
|
if (*size > retval)
|
||||||
size = retval;
|
*size = retval;
|
||||||
|
|
||||||
if (flags & PRB_FLAG_PUSH_SIZE) {
|
if (flags & PRB_FLAG_PUSH_SIZE && res == PPM_SUCCESS) {
|
||||||
res = bpf_push_u32_to_ring(data, (uint32_t)size);
|
res = bpf_push_u32_to_ring(data, (uint32_t)*size);
|
||||||
CHECK_RES(res);
|
CHECK_RES(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & PRB_FLAG_PUSH_DATA) {
|
if (flags & PRB_FLAG_PUSH_DATA) {
|
||||||
if (size > 0) {
|
if (*size > 0) {
|
||||||
unsigned long off = _READ(data->state->tail_ctx.curoff);
|
unsigned long off = _READ(data->state->tail_ctx.curoff);
|
||||||
unsigned long remaining = size;
|
unsigned long remaining = *size;
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
#pragma unroll
|
#pragma unroll
|
||||||
|
@ -653,11 +655,11 @@ static __always_inline int bpf_parse_readv_writev_bufs(struct filler_data *data,
|
||||||
if (to_read)
|
if (to_read)
|
||||||
if (bpf_probe_read_user(&data->buf[off_bounded],
|
if (bpf_probe_read_user(&data->buf[off_bounded],
|
||||||
((to_read - 1) & SCRATCH_SIZE_HALF) + 1,
|
((to_read - 1) & SCRATCH_SIZE_HALF) + 1,
|
||||||
iov[j].iov_base))
|
(void*)iov[j].iov_base))
|
||||||
#else
|
#else
|
||||||
if (bpf_probe_read_user(&data->buf[off_bounded],
|
if (bpf_probe_read_user(&data->buf[off_bounded],
|
||||||
to_read & SCRATCH_SIZE_HALF,
|
to_read & SCRATCH_SIZE_HALF,
|
||||||
iov[j].iov_base))
|
(void*)iov[j].iov_base))
|
||||||
#endif
|
#endif
|
||||||
return PPM_FAILURE_INVALID_USER_MEMORY;
|
return PPM_FAILURE_INVALID_USER_MEMORY;
|
||||||
|
|
||||||
|
@ -665,14 +667,144 @@ static __always_inline int bpf_parse_readv_writev_bufs(struct filler_data *data,
|
||||||
off += to_read;
|
off += to_read;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
size = 0;
|
*size = 0;
|
||||||
|
}
|
||||||
|
return PPM_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
static __always_inline int bpf_parse_readv_writev_bufs_32(struct filler_data *data,
|
||||||
|
const void __user *iovsrc,
|
||||||
|
unsigned long iovcnt,
|
||||||
|
long retval,
|
||||||
|
int flags,
|
||||||
|
unsigned long *size)
|
||||||
|
{
|
||||||
|
const struct compat_iovec *compat_iov;
|
||||||
|
int res = PPM_SUCCESS;
|
||||||
|
unsigned long copylen;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
copylen = iovcnt * sizeof(struct compat_iovec);
|
||||||
|
compat_iov = (const struct compat_iovec *)data->tmp_scratch;
|
||||||
|
|
||||||
|
if (copylen > SCRATCH_SIZE_MAX)
|
||||||
|
{
|
||||||
|
return PPM_FAILURE_FRAME_SCRATCH_MAP_FULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BPF_FORBIDS_ZERO_ACCESS
|
||||||
|
if (copylen)
|
||||||
|
if (bpf_probe_read_user((void *)compat_iov,
|
||||||
|
((copylen - 1) & SCRATCH_SIZE_MAX) + 1,
|
||||||
|
(void *)iovsrc))
|
||||||
|
#else
|
||||||
|
if (bpf_probe_read_user((void *)compat_iov,
|
||||||
|
copylen & SCRATCH_SIZE_MAX,
|
||||||
|
(void *)iovsrc))
|
||||||
|
#endif
|
||||||
|
return PPM_FAILURE_INVALID_USER_MEMORY;
|
||||||
|
|
||||||
|
#pragma unroll
|
||||||
|
for (j = 0; j < MAX_IOVCNT_COMPAT; ++j) {
|
||||||
|
if (j == iovcnt)
|
||||||
|
break;
|
||||||
|
// BPF seems to require a hard limit to avoid overflows
|
||||||
|
if (*size == LONG_MAX)
|
||||||
|
break;
|
||||||
|
|
||||||
|
*size += compat_iov[j].iov_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flags & PRB_FLAG_IS_WRITE) == 0)
|
||||||
|
if (*size > retval)
|
||||||
|
*size = retval;
|
||||||
|
|
||||||
|
if (flags & PRB_FLAG_PUSH_SIZE && res == PPM_SUCCESS) {
|
||||||
|
res = bpf_push_u32_to_ring(data, (uint32_t)*size);
|
||||||
|
CHECK_RES(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & PRB_FLAG_PUSH_DATA) {
|
||||||
|
if (*size > 0) {
|
||||||
|
unsigned long off = _READ(data->state->tail_ctx.curoff);
|
||||||
|
unsigned long remaining = *size;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
// The 14 iovec count limit is due to old kernels verifiers
|
||||||
|
// complaining.
|
||||||
|
#pragma unroll
|
||||||
|
for (j = 0; j < MAX_IOVCNT_COMPAT; ++j) {
|
||||||
|
volatile unsigned int to_read;
|
||||||
|
|
||||||
|
if (j == iovcnt)
|
||||||
|
break;
|
||||||
|
|
||||||
|
unsigned long off_bounded = off & SCRATCH_SIZE_HALF;
|
||||||
|
if (off > SCRATCH_SIZE_HALF)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (compat_iov[j].iov_len <= remaining)
|
||||||
|
to_read = compat_iov[j].iov_len;
|
||||||
|
else
|
||||||
|
to_read = remaining;
|
||||||
|
|
||||||
|
if (to_read > SCRATCH_SIZE_HALF)
|
||||||
|
to_read = SCRATCH_SIZE_HALF;
|
||||||
|
|
||||||
|
#ifdef BPF_FORBIDS_ZERO_ACCESS
|
||||||
|
if (to_read)
|
||||||
|
if (bpf_probe_read_user(&data->buf[off_bounded],
|
||||||
|
((to_read - 1) & SCRATCH_SIZE_HALF) + 1,
|
||||||
|
(void*)compat_iov[j].iov_base))
|
||||||
|
#else
|
||||||
|
if (bpf_probe_read_user(&data->buf[off_bounded],
|
||||||
|
to_read & SCRATCH_SIZE_HALF,
|
||||||
|
(void*)compat_iov[j].iov_base))
|
||||||
|
#endif
|
||||||
|
return PPM_FAILURE_INVALID_USER_MEMORY;
|
||||||
|
|
||||||
|
remaining -= to_read;
|
||||||
|
off += to_read;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PPM_SUCCESS;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static __always_inline int bpf_parse_readv_writev_bufs(struct filler_data *data,
|
||||||
|
const void __user *iovsrc,
|
||||||
|
unsigned long iovcnt,
|
||||||
|
long retval,
|
||||||
|
int flags)
|
||||||
|
{
|
||||||
|
unsigned long size = 0;
|
||||||
|
int res = PPM_SUCCESS;
|
||||||
|
if (!bpf_in_ia32_syscall())
|
||||||
|
{
|
||||||
|
res = bpf_parse_readv_writev_bufs_64(data, iovsrc, iovcnt, retval, flags, &size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
res = bpf_parse_readv_writev_bufs_32(data, iovsrc, iovcnt, retval, flags, &size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if(flags & PRB_FLAG_PUSH_DATA && res == PPM_SUCCESS)
|
||||||
|
{
|
||||||
data->fd = bpf_syscall_get_argument(data, 0);
|
data->fd = bpf_syscall_get_argument(data, 0);
|
||||||
data->curarg_already_on_frame = true;
|
data->curarg_already_on_frame = true;
|
||||||
return __bpf_val_to_ring(data, 0, size, PT_BYTEBUF, -1, true, KERNEL);
|
return __bpf_val_to_ring(data, 0, size, PT_BYTEBUF, -1, true, KERNEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -808,6 +940,8 @@ FILLER(sys_writev_pwritev_x, true)
|
||||||
|
|
||||||
static __always_inline int timespec_parse(struct filler_data *data,
|
static __always_inline int timespec_parse(struct filler_data *data,
|
||||||
unsigned long val)
|
unsigned long val)
|
||||||
|
{
|
||||||
|
if (!bpf_in_ia32_syscall())
|
||||||
{
|
{
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
|
||||||
struct __kernel_timespec ts = {};
|
struct __kernel_timespec ts = {};
|
||||||
|
@ -817,6 +951,13 @@ static __always_inline int timespec_parse(struct filler_data *data,
|
||||||
bpf_probe_read_user(&ts, sizeof(ts), (void *)val);
|
bpf_probe_read_user(&ts, sizeof(ts), (void *)val);
|
||||||
return bpf_push_u64_to_ring(data, ((uint64_t)ts.tv_sec) * 1000000000 + ts.tv_nsec);
|
return bpf_push_u64_to_ring(data, ((uint64_t)ts.tv_sec) * 1000000000 + ts.tv_nsec);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
old_timespec32 ts = {};
|
||||||
|
bpf_probe_read_user(&ts, sizeof(ts), (void *)val);
|
||||||
|
return bpf_push_u64_to_ring(data, ((uint32_t)ts.tv_sec) * 1000000000 + ts.tv_nsec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FILLER(sys_nanosleep_e, true)
|
FILLER(sys_nanosleep_e, true)
|
||||||
{
|
{
|
||||||
|
@ -3071,12 +3212,10 @@ FILLER(sys_generic, true)
|
||||||
// if we are in ia32 syscall sys_{enter,exit} already
|
// if we are in ia32 syscall sys_{enter,exit} already
|
||||||
// validated the converted 32bit->64bit syscall ID for us,
|
// validated the converted 32bit->64bit syscall ID for us,
|
||||||
// otherwise the event would've been discarded.
|
// otherwise the event would've been discarded.
|
||||||
#if defined(CONFIG_X86_64) && defined(CONFIG_IA32_EMULATION)
|
|
||||||
if (bpf_in_ia32_syscall())
|
if (bpf_in_ia32_syscall())
|
||||||
{
|
{
|
||||||
native_id = convert_ia32_to_64(native_id);
|
native_id = convert_ia32_to_64(native_id);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
sc_evt = get_syscall_info(native_id);
|
sc_evt = get_syscall_info(native_id);
|
||||||
if (!sc_evt) {
|
if (!sc_evt) {
|
||||||
|
|
|
@ -79,4 +79,15 @@ struct modern_bpf__kernel_timex_timeval
|
||||||
long long int tv_usec;
|
long long int tv_usec;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is equivalent to old_timespec32 or compat_timespec. Some old distros
|
||||||
|
* don't define old_timespec32 (e.g. centos 8 with 4.18 kernel), so we define
|
||||||
|
* it here.
|
||||||
|
*/
|
||||||
|
struct modern_bpf__kernel_timespec_ia32
|
||||||
|
{
|
||||||
|
int tv_sec;
|
||||||
|
int tv_nsec;
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* __STRUCT_FLAVORS_H__ */
|
#endif /* __STRUCT_FLAVORS_H__ */
|
||||||
|
|
|
@ -1003,10 +1003,11 @@ static __always_inline void auxmap__store_iovec_size_param(struct auxiliary_map
|
||||||
* @param iov_cnt number of `iovec` structs to be read from userspace.
|
* @param iov_cnt number of `iovec` structs to be read from userspace.
|
||||||
* @param len_to_read imposed snaplen.
|
* @param len_to_read imposed snaplen.
|
||||||
*/
|
*/
|
||||||
static __always_inline void auxmap__store_iovec_data_param(struct auxiliary_map *auxmap, unsigned long iov_pointer, unsigned long iov_cnt, unsigned long len_to_read)
|
static __always_inline void auxmap__store_iovec_data_param_64(struct auxiliary_map *auxmap, unsigned long iov_pointer, unsigned long iov_cnt, unsigned long len_to_read)
|
||||||
{
|
{
|
||||||
/* We use the second part of our auxmap as a scratch space. */
|
/* We use the second part of our auxmap as a scratch space. */
|
||||||
uint32_t total_iovec_size = iov_cnt * bpf_core_type_size(struct iovec);
|
unsigned long total_iovec_size = iov_cnt * bpf_core_type_size(struct iovec);
|
||||||
|
|
||||||
if(bpf_probe_read_user((void *)&auxmap->data[MAX_PARAM_SIZE],
|
if(bpf_probe_read_user((void *)&auxmap->data[MAX_PARAM_SIZE],
|
||||||
SAFE_ACCESS(total_iovec_size),
|
SAFE_ACCESS(total_iovec_size),
|
||||||
(void *)iov_pointer))
|
(void *)iov_pointer))
|
||||||
|
@ -1016,7 +1017,7 @@ static __always_inline void auxmap__store_iovec_data_param(struct auxiliary_map
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t total_size_to_read = 0;
|
unsigned long total_size_to_read = 0;
|
||||||
|
|
||||||
/* Pointer to iovec structs */
|
/* Pointer to iovec structs */
|
||||||
const struct iovec *iovec = (const struct iovec *)&auxmap->data[MAX_PARAM_SIZE];
|
const struct iovec *iovec = (const struct iovec *)&auxmap->data[MAX_PARAM_SIZE];
|
||||||
|
@ -1050,6 +1051,66 @@ static __always_inline void auxmap__store_iovec_data_param(struct auxiliary_map
|
||||||
push__param_len(auxmap->data, &auxmap->lengths_pos, total_size_to_read);
|
push__param_len(auxmap->data, &auxmap->lengths_pos, total_size_to_read);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static __always_inline void auxmap__store_iovec_data_param_32(struct auxiliary_map *auxmap, unsigned long iov_pointer, unsigned long iov_cnt, unsigned long len_to_read)
|
||||||
|
{
|
||||||
|
/* We use the second part of our auxmap as a scratch space. */
|
||||||
|
unsigned long total_iovec_size = iov_cnt * bpf_core_type_size(struct compat_iovec);
|
||||||
|
|
||||||
|
if(bpf_probe_read_user((void *)&auxmap->data[MAX_PARAM_SIZE],
|
||||||
|
SAFE_ACCESS(total_iovec_size),
|
||||||
|
(void *)iov_pointer))
|
||||||
|
{
|
||||||
|
/* in case of NULL iovec vector we return an empty param */
|
||||||
|
push__param_len(auxmap->data, &auxmap->lengths_pos, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long total_size_to_read = 0;
|
||||||
|
|
||||||
|
/* Pointer to iovec structs */
|
||||||
|
const struct compat_iovec *compat_iovec = (const struct compat_iovec *)&auxmap->data[MAX_PARAM_SIZE];
|
||||||
|
uint64_t initial_payload_pos = auxmap->payload_pos;
|
||||||
|
for(int j = 0; j < MAX_IOVCNT; j++)
|
||||||
|
{
|
||||||
|
if(total_size_to_read > len_to_read)
|
||||||
|
{
|
||||||
|
/* If we break here it could be that `payload_pos` overcame the max `len_to_read` for this reason
|
||||||
|
* we have an enforcement after the for loop.
|
||||||
|
*/
|
||||||
|
total_size_to_read = len_to_read;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(j == iov_cnt)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t bytes_read = push__bytebuf(auxmap->data, &auxmap->payload_pos, (unsigned long)compat_iovec[j].iov_base, compat_iovec[j].iov_len, USER);
|
||||||
|
if(!bytes_read)
|
||||||
|
{
|
||||||
|
push__param_len(auxmap->data, &auxmap->lengths_pos, total_size_to_read);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
total_size_to_read += bytes_read;
|
||||||
|
}
|
||||||
|
/* We need this enforcement to be sure that we don't overcome the max `len_to_read` */
|
||||||
|
auxmap->payload_pos = initial_payload_pos + total_size_to_read;
|
||||||
|
push__param_len(auxmap->data, &auxmap->lengths_pos, total_size_to_read);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline void auxmap__store_iovec_data_param(struct auxiliary_map *auxmap, unsigned long iov_pointer, unsigned long iov_cnt, unsigned long len_to_read)
|
||||||
|
{
|
||||||
|
if(!bpf_in_ia32_syscall())
|
||||||
|
{
|
||||||
|
auxmap__store_iovec_data_param_64(auxmap, iov_pointer, iov_cnt, len_to_read);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auxmap__store_iovec_data_param_32(auxmap, iov_pointer, iov_cnt, len_to_read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Store the size extracted from a `user_msghdr` struct.
|
* @brief Store the size extracted from a `user_msghdr` struct.
|
||||||
* Please note: the size is an unsigned 32 bit value so
|
* Please note: the size is an unsigned 32 bit value so
|
||||||
|
|
|
@ -319,7 +319,16 @@ static __always_inline void ringbuf__store_iovec_size_param(struct ringbuf_struc
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t total_iovec_size = iov_cnt * bpf_core_type_size(struct iovec);
|
uint32_t total_iovec_size = 0;
|
||||||
|
if(!bpf_in_ia32_syscall())
|
||||||
|
{
|
||||||
|
total_iovec_size = iov_cnt * bpf_core_type_size(struct iovec);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
total_iovec_size = iov_cnt * bpf_core_type_size(struct compat_iovec);
|
||||||
|
}
|
||||||
|
|
||||||
if(bpf_probe_read_user((void *)&auxmap->data[0],
|
if(bpf_probe_read_user((void *)&auxmap->data[0],
|
||||||
SAFE_ACCESS(total_iovec_size),
|
SAFE_ACCESS(total_iovec_size),
|
||||||
(void *)iov_pointer))
|
(void *)iov_pointer))
|
||||||
|
@ -331,6 +340,8 @@ static __always_inline void ringbuf__store_iovec_size_param(struct ringbuf_struc
|
||||||
uint32_t total_size_to_read = 0;
|
uint32_t total_size_to_read = 0;
|
||||||
|
|
||||||
/* Pointer to iovec structs */
|
/* Pointer to iovec structs */
|
||||||
|
if(!bpf_in_ia32_syscall())
|
||||||
|
{
|
||||||
const struct iovec *iovec = (const struct iovec *)&auxmap->data[0];
|
const struct iovec *iovec = (const struct iovec *)&auxmap->data[0];
|
||||||
for(int j = 0; j < MAX_IOVCNT; j++)
|
for(int j = 0; j < MAX_IOVCNT; j++)
|
||||||
{
|
{
|
||||||
|
@ -340,5 +351,18 @@ static __always_inline void ringbuf__store_iovec_size_param(struct ringbuf_struc
|
||||||
}
|
}
|
||||||
total_size_to_read += iovec[j].iov_len;
|
total_size_to_read += iovec[j].iov_len;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const struct compat_iovec *iovec = (const struct compat_iovec *)&auxmap->data[0];
|
||||||
|
for(int j = 0; j < MAX_IOVCNT; j++)
|
||||||
|
{
|
||||||
|
if(j == iov_cnt)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
total_size_to_read += iovec[j].iov_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
ringbuf__store_u32(ringbuf, total_size_to_read);
|
ringbuf__store_u32(ringbuf, total_size_to_read);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,8 @@ int BPF_PROG(ppoll_e,
|
||||||
/* Parameter 2: timeout (type: PT_RELTIME) */
|
/* Parameter 2: timeout (type: PT_RELTIME) */
|
||||||
uint64_t nanosec = 0;
|
uint64_t nanosec = 0;
|
||||||
unsigned long ts_pointer = extract__syscall_argument(regs, 2);
|
unsigned long ts_pointer = extract__syscall_argument(regs, 2);
|
||||||
|
if(!bpf_in_ia32_syscall())
|
||||||
|
{
|
||||||
if(bpf_core_type_exists(struct __kernel_timespec))
|
if(bpf_core_type_exists(struct __kernel_timespec))
|
||||||
{
|
{
|
||||||
struct __kernel_timespec ts = {0};
|
struct __kernel_timespec ts = {0};
|
||||||
|
@ -50,6 +52,13 @@ int BPF_PROG(ppoll_e,
|
||||||
bpf_probe_read_user(&ts, sizeof(ts), (void *)ts_pointer);
|
bpf_probe_read_user(&ts, sizeof(ts), (void *)ts_pointer);
|
||||||
nanosec = ((uint64_t)ts.tv_sec) * SECOND_TO_NS + ts.tv_nsec;
|
nanosec = ((uint64_t)ts.tv_sec) * SECOND_TO_NS + ts.tv_nsec;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct modern_bpf__kernel_timespec_ia32 ts = {0};
|
||||||
|
bpf_probe_read_user(&ts, sizeof(ts), (void *)ts_pointer);
|
||||||
|
nanosec = ((uint32_t)ts.tv_sec) * SECOND_TO_NS + ts.tv_nsec;
|
||||||
|
}
|
||||||
auxmap__store_u64_param(auxmap, nanosec);
|
auxmap__store_u64_param(auxmap, nanosec);
|
||||||
|
|
||||||
/* Parameter 3: sigmask (type: PT_SIGSET) */
|
/* Parameter 3: sigmask (type: PT_SIGSET) */
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
pytest==7.1.1
|
pytest==7.1.1
|
||||||
pytest-html==3.1.1
|
pytest-html==3.1.1
|
||||||
docker==6.0.0
|
docker==6.0.0
|
||||||
|
requests==2.31.0
|
||||||
|
|
|
@ -32,6 +32,7 @@ configure_file (
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(libsinsp_e2e_tests
|
add_executable(libsinsp_e2e_tests
|
||||||
|
capture_to_file_test.cpp
|
||||||
container/container.cpp
|
container/container.cpp
|
||||||
container/container_cgroup.cpp
|
container/container_cgroup.cpp
|
||||||
container/docker_utils.cpp
|
container/docker_utils.cpp
|
||||||
|
@ -50,6 +51,7 @@ add_executable(libsinsp_e2e_tests
|
||||||
threadinfo.cpp
|
threadinfo.cpp
|
||||||
thread_state.cpp
|
thread_state.cpp
|
||||||
udp_client_server.cpp
|
udp_client_server.cpp
|
||||||
|
unix_client_server.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(BUILD_BPF)
|
if(BUILD_BPF)
|
||||||
|
@ -103,3 +105,8 @@ file(COPY
|
||||||
DESTINATION
|
DESTINATION
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/resources/
|
${CMAKE_CURRENT_BINARY_DIR}/resources/
|
||||||
)
|
)
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND tar xzf ${CMAKE_CURRENT_BINARY_DIR}/resources/fake-proc.tar.gz
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/resources/
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
/*
|
||||||
|
Copyright (C) 2024 The Falco Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License 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.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sys_call_test.h"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <libsinsp/sinsp.h>
|
||||||
|
|
||||||
|
TEST_F(sys_call_test, can_consume_a_capture_file)
|
||||||
|
{
|
||||||
|
int callnum = 0;
|
||||||
|
|
||||||
|
event_filter_t filter = [&](sinsp_evt* evt)
|
||||||
|
{
|
||||||
|
std::string evt_name(evt->get_name());
|
||||||
|
return evt_name.find("stat") != std::string::npos &&
|
||||||
|
m_tid_filter(evt) && evt->get_direction() == SCAP_ED_OUT;
|
||||||
|
};
|
||||||
|
|
||||||
|
run_callback_t test = [](concurrent_object_handle<sinsp> inspector_handle)
|
||||||
|
{
|
||||||
|
struct stat sb;
|
||||||
|
for(int i = 0; i < 100; i++)
|
||||||
|
{
|
||||||
|
stat("/tmp", &sb);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
captured_event_callback_t callback = [&](const callback_param& param) { callnum++; };
|
||||||
|
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
|
||||||
|
EXPECT_EQ(100, callnum);
|
||||||
|
|
||||||
|
sinsp inspector;
|
||||||
|
sinsp_evt* event;
|
||||||
|
|
||||||
|
const ::testing::TestInfo* const test_info =
|
||||||
|
::testing::UnitTest::GetInstance()->current_test_info();
|
||||||
|
auto filename = std::string(LIBSINSP_TEST_CAPTURES_PATH) + test_info->test_case_name() + "_" +
|
||||||
|
test_info->name() + ".scap";
|
||||||
|
inspector.open_savefile(filename);
|
||||||
|
callnum = 0;
|
||||||
|
int32_t res;
|
||||||
|
while((res = inspector.next(&event)) != SCAP_EOF)
|
||||||
|
{
|
||||||
|
ASSERT_EQ(SCAP_SUCCESS, res);
|
||||||
|
std::string evt_name(event->get_name());
|
||||||
|
if(evt_name.find("stat") != std::string::npos &&
|
||||||
|
m_tid_filter(event) && event->get_direction() == SCAP_ED_OUT)
|
||||||
|
{
|
||||||
|
callnum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(SCAP_EOF, res);
|
||||||
|
ASSERT_EQ(100, callnum);
|
||||||
|
}
|
|
@ -446,7 +446,7 @@ TEST_F(sys_call_test, container_libvirt)
|
||||||
|
|
||||||
if (system("virsh --help > /dev/null 2>&1") != 0)
|
if (system("virsh --help > /dev/null 2>&1") != 0)
|
||||||
{
|
{
|
||||||
printf("libvirt not installed, skipping test\n");
|
GTEST_SKIP() << "libvirt not installed, skipping test";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -722,6 +722,83 @@ static void healthcheck_helper(
|
||||||
ASSERT_EQ(cstate.healthcheck_seen, expect_healthcheck) << capture_stats_str;
|
ASSERT_EQ(cstate.healthcheck_seen, expect_healthcheck) << capture_stats_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void healthcheck_tracefile_helper(
|
||||||
|
const std::string& dockerfile,
|
||||||
|
bool expect_healthcheck,
|
||||||
|
sinsp_threadinfo::command_category expected_cat = sinsp_threadinfo::CAT_HEALTHCHECK)
|
||||||
|
{
|
||||||
|
container_state cstate;
|
||||||
|
|
||||||
|
std::string build_cmdline("cd " LIBSINSP_TEST_RESOURCES_PATH "/docker/health_dockerfiles && docker build -t cont_health_ut_img -f "
|
||||||
|
+ dockerfile + " . > /dev/null 2>&1");
|
||||||
|
ASSERT_TRUE(system(build_cmdline.c_str()) == 0);
|
||||||
|
|
||||||
|
run_callback_t test = [](concurrent_object_handle<sinsp> inspector_handle)
|
||||||
|
{
|
||||||
|
// --network=none speeds up the container setup a bit.
|
||||||
|
ASSERT_TRUE((system("docker run --rm --network=none --name cont_health_ut cont_health_ut_img "
|
||||||
|
"/bin/sh -c '/bin/sleep 10' > /dev/null 2>&1")) == 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
event_filter_t filter = [&](sinsp_evt* evt)
|
||||||
|
{
|
||||||
|
std::string evt_name(evt->get_name());
|
||||||
|
return evt_name.find("execve") != std::string::npos &&
|
||||||
|
evt->get_direction() == SCAP_ED_OUT;
|
||||||
|
};
|
||||||
|
|
||||||
|
captured_event_callback_t callback = [&](const callback_param& param) {return;};
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
|
||||||
|
|
||||||
|
// Now reread the file we just wrote and pass it through
|
||||||
|
// update_container_state.
|
||||||
|
|
||||||
|
const ::testing::TestInfo* const test_info =
|
||||||
|
::testing::UnitTest::GetInstance()->current_test_info();
|
||||||
|
auto dumpfile = std::string(LIBSINSP_TEST_CAPTURES_PATH) + test_info->test_case_name() + "_" +
|
||||||
|
test_info->name() + ".scap";
|
||||||
|
|
||||||
|
sinsp inspector;
|
||||||
|
inspector.set_hostname_and_port_resolution_mode(false);
|
||||||
|
inspector.set_filter("evt.type=execve and evt.dir=<");
|
||||||
|
inspector.open_savefile(dumpfile);
|
||||||
|
inspector.start_capture();
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
sinsp_evt* ev;
|
||||||
|
int32_t res = inspector.next(&ev);
|
||||||
|
|
||||||
|
if (res == SCAP_TIMEOUT)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (res == SCAP_FILTERED_EVENT)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (res == SCAP_EOF)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(res == SCAP_SUCCESS);
|
||||||
|
|
||||||
|
update_container_state(&inspector, ev, cstate, expected_cat);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string capture_stats_str = capture_stats(&inspector);
|
||||||
|
|
||||||
|
inspector.stop_capture();
|
||||||
|
inspector.close();
|
||||||
|
|
||||||
|
ASSERT_TRUE(cstate.root_cmd_seen) << capture_stats_str;
|
||||||
|
ASSERT_TRUE(cstate.second_cmd_seen) << capture_stats_str;
|
||||||
|
ASSERT_EQ(cstate.container_w_health_probe, expect_healthcheck) << capture_stats_str;
|
||||||
|
ASSERT_EQ(cstate.healthcheck_seen, expect_healthcheck) << capture_stats_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Run container w/o health check, should not find any health check
|
// Run container w/o health check, should not find any health check
|
||||||
// for the container. Should not identify either the entrypoint
|
// for the container. Should not identify either the entrypoint
|
||||||
// or a second process spawned after as a health check process.
|
// or a second process spawned after as a health check process.
|
||||||
|
@ -791,6 +868,33 @@ TEST_F(sys_call_test, docker_container_readiness_probe)
|
||||||
sinsp_threadinfo::CAT_READINESS_PROBE);
|
sinsp_threadinfo::CAT_READINESS_PROBE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Identical to above tests, but read events from a trace file instead
|
||||||
|
// of live. Only doing selected cases.
|
||||||
|
TEST_F(sys_call_test, docker_container_healthcheck_trace)
|
||||||
|
{
|
||||||
|
healthcheck_tracefile_helper("Dockerfile.healthcheck", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(sys_call_test, docker_container_healthcheck_cmd_overlap_trace)
|
||||||
|
{
|
||||||
|
healthcheck_tracefile_helper("Dockerfile.healthcheck_cmd_overlap", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(sys_call_test, docker_container_liveness_probe_trace)
|
||||||
|
{
|
||||||
|
healthcheck_tracefile_helper("Dockerfile.healthcheck_liveness",
|
||||||
|
true,
|
||||||
|
sinsp_threadinfo::CAT_LIVENESS_PROBE);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(sys_call_test, docker_container_readiness_probe_trace)
|
||||||
|
{
|
||||||
|
healthcheck_tracefile_helper("Dockerfile.healthcheck_readiness",
|
||||||
|
true,
|
||||||
|
sinsp_threadinfo::CAT_READINESS_PROBE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_F(sys_call_test, docker_container_large_json)
|
TEST_F(sys_call_test, docker_container_large_json)
|
||||||
{
|
{
|
||||||
bool saw_container_evt = false;
|
bool saw_container_evt = false;
|
||||||
|
|
|
@ -125,7 +125,7 @@ void event_capture::capture()
|
||||||
if (SCAP_SUCCESS == next_result)
|
if (SCAP_SUCCESS == next_result)
|
||||||
{
|
{
|
||||||
result = handle_event(event);
|
result = handle_event(event);
|
||||||
if (m_mode != SINSP_MODE_NODRIVER)
|
if (m_mode != SINSP_MODE_NODRIVER && m_dump)
|
||||||
{
|
{
|
||||||
dumper->dump(event);
|
dumper->dump(event);
|
||||||
}
|
}
|
||||||
|
@ -169,7 +169,10 @@ void event_capture::capture()
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if(m_dump)
|
||||||
|
{
|
||||||
dumper->dump(event);
|
dumper->dump(event);
|
||||||
|
}
|
||||||
result = handle_event(event);
|
result = handle_event(event);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -177,7 +180,6 @@ void event_capture::capture()
|
||||||
while (SCAP_SUCCESS == get_inspector()->next(&event))
|
while (SCAP_SUCCESS == get_inspector()->next(&event))
|
||||||
{
|
{
|
||||||
// just consume the remaining events
|
// just consume the remaining events
|
||||||
dumper->dump(event);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,7 +147,8 @@ public:
|
||||||
uint64_t thread_timeout_ns = (uint64_t)60 * 1000 * 1000 * 1000,
|
uint64_t thread_timeout_ns = (uint64_t)60 * 1000 * 1000 * 1000,
|
||||||
uint64_t inactive_thread_scan_time_ns = (uint64_t)60 * 1000 * 1000 * 1000,
|
uint64_t inactive_thread_scan_time_ns = (uint64_t)60 * 1000 * 1000 * 1000,
|
||||||
sinsp_mode_t mode = SINSP_MODE_LIVE,
|
sinsp_mode_t mode = SINSP_MODE_LIVE,
|
||||||
uint64_t max_timeouts = 3)
|
uint64_t max_timeouts = 3,
|
||||||
|
bool dump = true)
|
||||||
{
|
{
|
||||||
event_capture capturing;
|
event_capture capturing;
|
||||||
{ // Synchronized section
|
{ // Synchronized section
|
||||||
|
@ -162,6 +163,7 @@ public:
|
||||||
capturing.m_thread_timeout_ns = thread_timeout_ns;
|
capturing.m_thread_timeout_ns = thread_timeout_ns;
|
||||||
capturing.m_inactive_thread_scan_time_ns = inactive_thread_scan_time_ns;
|
capturing.m_inactive_thread_scan_time_ns = inactive_thread_scan_time_ns;
|
||||||
capturing.m_max_timeouts = max_timeouts;
|
capturing.m_max_timeouts = max_timeouts;
|
||||||
|
capturing.m_dump = dump;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -233,4 +235,5 @@ private:
|
||||||
static bool inspector_ok;
|
static bool inspector_ok;
|
||||||
sinsp_mode_t m_mode;
|
sinsp_mode_t m_mode;
|
||||||
uint64_t m_max_timeouts;
|
uint64_t m_max_timeouts;
|
||||||
|
bool m_dump;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1465,7 +1465,11 @@ TEST_F(sys_call_test, large_read_write)
|
||||||
inspector->set_snaplen(DEFAULT_SNAPLEN);
|
inspector->set_snaplen(DEFAULT_SNAPLEN);
|
||||||
};
|
};
|
||||||
|
|
||||||
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter, setup, cleanup); });
|
// We don't dump events to scap files, otherwise we could stuck with modern bpf.
|
||||||
|
ASSERT_NO_FATAL_FAILURE({event_capture::run(test, callback, filter, setup,
|
||||||
|
cleanup, event_capture::always_continue, 131072,
|
||||||
|
(uint64_t)60 * 1000 * 1000 * 1000, (uint64_t)60 * 1000 * 1000 * 1000,
|
||||||
|
SINSP_MODE_LIVE, 3, false); });
|
||||||
|
|
||||||
EXPECT_EQ(4, callnum);
|
EXPECT_EQ(4, callnum);
|
||||||
}
|
}
|
||||||
|
@ -1603,7 +1607,11 @@ TEST_F(sys_call_test, large_readv_writev)
|
||||||
inspector->set_snaplen(DEFAULT_SNAPLEN);
|
inspector->set_snaplen(DEFAULT_SNAPLEN);
|
||||||
};
|
};
|
||||||
|
|
||||||
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter, setup, cleanup); });
|
// We don't dump events to scap files, otherwise we could stuck with modern bpf.
|
||||||
|
ASSERT_NO_FATAL_FAILURE({event_capture::run(test, callback, filter, setup,
|
||||||
|
cleanup, event_capture::always_continue, 131072,
|
||||||
|
(uint64_t)60 * 1000 * 1000 * 1000, (uint64_t)60 * 1000 * 1000 * 1000,
|
||||||
|
SINSP_MODE_LIVE, 3, false); });
|
||||||
|
|
||||||
EXPECT_EQ(4, callnum);
|
EXPECT_EQ(4, callnum);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
foreach(
|
||||||
|
dockerfile
|
||||||
|
Dockerfile.healthcheck
|
||||||
|
Dockerfile.healthcheck_shell
|
||||||
|
Dockerfile.healthcheck_cmd_overlap
|
||||||
|
Dockerfile.healthcheck_liveness
|
||||||
|
Dockerfile.healthcheck_readiness
|
||||||
|
Dockerfile.no_healthcheck
|
||||||
|
Dockerfile.none_healthcheck
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/${dockerfile} ${CMAKE_CURRENT_BINARY_DIR}/${dockerfile}
|
||||||
|
COPYONLY
|
||||||
|
)
|
||||||
|
|
||||||
|
endforeach(dockerfile)
|
|
@ -0,0 +1,3 @@
|
||||||
|
FROM busybox
|
||||||
|
RUN cp /bin/true /bin/ut-health-check
|
||||||
|
HEALTHCHECK --interval=0.5s CMD ["/bin/ut-health-check"]
|
|
@ -0,0 +1,2 @@
|
||||||
|
FROM busybox
|
||||||
|
HEALTHCHECK --interval=0.5s CMD ["/bin/sh", "-c", "/bin/sleep 10"]
|
|
@ -0,0 +1,8 @@
|
||||||
|
FROM busybox
|
||||||
|
RUN cp /bin/true /bin/ut-health-check
|
||||||
|
|
||||||
|
# This container runs a docker healthcheck, but due to the
|
||||||
|
# annotation.... label, it gets interpretated as if it were a k8s
|
||||||
|
# liveness check.
|
||||||
|
HEALTHCHECK --interval=0.5s CMD ["/bin/ut-health-check"]
|
||||||
|
LABEL annotation.kubectl.kubernetes.io/last-applied-configuration="{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"name\":\"mysql-app\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"MYSQL_ROOT_PASSWORD\",\"value\":\"no\"}],\"image\":\"mstemm/mysql:healthcheck\",\"livenessProbe\":{\"exec\":{\"command\":[\"/bin/ut-health-check\"]},\"initialDelaySeconds\":5,\"periodSeconds\":5},\"name\":\"mysql\"}]}}\n"
|
|
@ -0,0 +1,8 @@
|
||||||
|
FROM busybox
|
||||||
|
RUN cp /bin/true /bin/ut-health-check
|
||||||
|
|
||||||
|
# This container runs a docker healthcheck, but due to the
|
||||||
|
# annotation.... label, it gets interpretated as if it were a k8s
|
||||||
|
# readiness check.
|
||||||
|
HEALTHCHECK --interval=0.5s CMD ["/bin/ut-health-check"]
|
||||||
|
LABEL annotation.kubectl.kubernetes.io/last-applied-configuration="{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"name\":\"mysql-app\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"env\":[{\"name\":\"MYSQL_ROOT_PASSWORD\",\"value\":\"no\"}],\"image\":\"mstemm/mysql:healthcheck\",\"readinessProbe\":{\"exec\":{\"command\":[\"/bin/ut-health-check\"]},\"initialDelaySeconds\":5,\"periodSeconds\":5},\"name\":\"mysql\"}]}}\n"
|
|
@ -0,0 +1,3 @@
|
||||||
|
FROM busybox
|
||||||
|
RUN cp /bin/true /bin/ut-health-check
|
||||||
|
HEALTHCHECK --interval=0.5s CMD /bin/ut-health-check
|
|
@ -0,0 +1 @@
|
||||||
|
FROM busybox:latest
|
|
@ -0,0 +1,2 @@
|
||||||
|
FROM busybox:latest
|
||||||
|
HEALTHCHECK NONE
|
Binary file not shown.
|
@ -0,0 +1,43 @@
|
||||||
|
#!/usr/bin/python2
|
||||||
|
# coding: utf-8 -*-
|
||||||
|
import socket
|
||||||
|
import os, os.path
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
|
||||||
|
PAYLOAD = "0123456789QWERTYUIOPASDFGHJKLZXCVBNM"
|
||||||
|
NAME = "/tmp/python_unix_sockets_example"
|
||||||
|
|
||||||
|
if sys.argv[1] == 'server':
|
||||||
|
if os.path.exists(NAME):
|
||||||
|
os.remove(NAME)
|
||||||
|
|
||||||
|
server = socket.socket( socket.AF_UNIX, socket.SOCK_STREAM )
|
||||||
|
server.bind(NAME)
|
||||||
|
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
|
||||||
|
|
||||||
|
print "STARTED"
|
||||||
|
server.listen(5)
|
||||||
|
|
||||||
|
connect, address = server.accept()
|
||||||
|
resp = connect.recv( 1024 )
|
||||||
|
connect.send(resp)
|
||||||
|
connect.close()
|
||||||
|
server.close()
|
||||||
|
os.remove(NAME)
|
||||||
|
|
||||||
|
else:
|
||||||
|
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
|
||||||
|
|
||||||
|
if os.path.exists(NAME):
|
||||||
|
client = socket.socket( socket.AF_UNIX, socket.SOCK_STREAM )
|
||||||
|
client.connect(NAME)
|
||||||
|
|
||||||
|
print "STARTED"
|
||||||
|
|
||||||
|
client.send(PAYLOAD)
|
||||||
|
resp = client.recv(1024)
|
||||||
|
client.close()
|
||||||
|
|
||||||
|
else:
|
||||||
|
print >> sys.stderr, "Couldn't Connect!"
|
|
@ -30,11 +30,13 @@ limitations under the License.
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <ext/stdio_filebuf.h>
|
#include <ext/stdio_filebuf.h>
|
||||||
|
|
||||||
subprocess::subprocess(std::string command, std::vector<std::string> arguments, bool start_now)
|
subprocess::subprocess(std::string command, std::vector<std::string> arguments,
|
||||||
: m_pid(-1)
|
bool start_now, int retry_attempts):
|
||||||
|
m_pid(-1),
|
||||||
|
m_retry_attemps(retry_attempts),
|
||||||
|
m_command(command),
|
||||||
|
m_args(arguments)
|
||||||
{
|
{
|
||||||
m_command = command;
|
|
||||||
m_args = arguments;
|
|
||||||
if(start_now)
|
if(start_now)
|
||||||
{
|
{
|
||||||
start();
|
start();
|
||||||
|
@ -60,14 +62,18 @@ void subprocess::wait_for_start()
|
||||||
timeout.tv_usec = 0;
|
timeout.tv_usec = 0;
|
||||||
|
|
||||||
int result = select(m_out_pipe[0] + 1, &read_set, nullptr, nullptr, &timeout);
|
int result = select(m_out_pipe[0] + 1, &read_set, nullptr, nullptr, &timeout);
|
||||||
|
int attempt = 0;
|
||||||
|
|
||||||
|
while(attempt < m_retry_attemps)
|
||||||
|
{
|
||||||
switch(result)
|
switch(result)
|
||||||
{
|
{
|
||||||
case -1:
|
case -1:
|
||||||
perror("select");
|
perror("select");
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
std::cerr << "Timeout waiting for process to start." << std::endl;
|
std::cerr << "Timeout waiting for process to start. Retry n."
|
||||||
|
<< (attempt + 1) << "/" << m_retry_attemps << std::endl;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (!FD_ISSET(m_out_pipe[0], &read_set)) {
|
if (!FD_ISSET(m_out_pipe[0], &read_set)) {
|
||||||
|
@ -75,6 +81,8 @@ void subprocess::wait_for_start()
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
attempt++;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,8 +31,8 @@ limitations under the License.
|
||||||
|
|
||||||
class subprocess {
|
class subprocess {
|
||||||
public:
|
public:
|
||||||
|
subprocess(std::string command, std::vector<std::string> arguments,
|
||||||
subprocess(std::string command, std::vector<std::string> arguments, bool start_now=true);
|
bool start_now=true, int retry_attempts=3);
|
||||||
~subprocess();
|
~subprocess();
|
||||||
|
|
||||||
void wait_for_start();
|
void wait_for_start();
|
||||||
|
@ -51,6 +51,7 @@ class subprocess {
|
||||||
pid_t m_pid;
|
pid_t m_pid;
|
||||||
int m_in_pipe[2];
|
int m_in_pipe[2];
|
||||||
int m_out_pipe[2];
|
int m_out_pipe[2];
|
||||||
|
int m_retry_attemps;
|
||||||
|
|
||||||
std::ostream* m_in_stream;
|
std::ostream* m_in_stream;
|
||||||
std::istream* m_out_stream;
|
std::istream* m_out_stream;
|
||||||
|
|
|
@ -23,6 +23,7 @@ limitations under the License.
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <libscap/scap-int.h>
|
#include <libscap/scap-int.h>
|
||||||
|
#include <libscap/scap_engines.h>
|
||||||
#include <libscap/scap_platform.h>
|
#include <libscap/scap_platform.h>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
@ -351,6 +352,92 @@ TEST_F(sys_call_test, close_badfd_dropping)
|
||||||
EXPECT_EQ(0, callnum);
|
EXPECT_EQ(0, callnum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The poll syscall is not defined on arm64.
|
||||||
|
#if !defined(__aarch64__)
|
||||||
|
TEST_F(sys_call_test, poll_timeout)
|
||||||
|
{
|
||||||
|
int callnum = 0;
|
||||||
|
|
||||||
|
event_filter_t filter = [&](sinsp_evt* evt)
|
||||||
|
{
|
||||||
|
uint16_t type = evt->get_type();
|
||||||
|
auto ti = evt->get_thread_info(false);
|
||||||
|
return (type == PPME_SYSCALL_POLL_E ||
|
||||||
|
type == PPME_SYSCALL_POLL_X) &&
|
||||||
|
ti->m_comm == "test_helper";
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string my_pipe[2];
|
||||||
|
|
||||||
|
run_callback_t test = [&](concurrent_object_handle<sinsp> inspector_handle)
|
||||||
|
{
|
||||||
|
subprocess handle(LIBSINSP_TEST_PATH "/test_helper", {"poll_timeout"});
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << handle.out();
|
||||||
|
my_pipe[0] = ss.str();
|
||||||
|
ss.clear();
|
||||||
|
ss.str(" ");
|
||||||
|
ss << handle.out();
|
||||||
|
my_pipe[1] = ss.str();
|
||||||
|
ss.clear();
|
||||||
|
ss.str(" ");
|
||||||
|
handle.wait();
|
||||||
|
};
|
||||||
|
captured_event_callback_t callback = [&](const callback_param& param)
|
||||||
|
{
|
||||||
|
sinsp_evt* e = param.m_evt;
|
||||||
|
uint16_t type = e->get_type();
|
||||||
|
|
||||||
|
if (type == PPME_SYSCALL_POLL_E)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// stdin and stdout can be a file or a fifo depending
|
||||||
|
// on how the tests are invoked
|
||||||
|
//
|
||||||
|
std::string fds = e->get_param_value_str("fds");
|
||||||
|
std::string expected_fds = my_pipe[0] + ":p1 " +
|
||||||
|
my_pipe[1] + ":p4";
|
||||||
|
EXPECT_EQ(expected_fds, fds)
|
||||||
|
<< "Value of fds is not one of expected values: " << fds;
|
||||||
|
EXPECT_EQ("20", e->get_param_value_str("timeout"));
|
||||||
|
callnum++;
|
||||||
|
}
|
||||||
|
else if (type == PPME_SYSCALL_POLL_X)
|
||||||
|
{
|
||||||
|
std::string fds = e->get_param_value_str("fds");
|
||||||
|
std::string expected_fds = my_pipe[0] + ":p0 " +
|
||||||
|
my_pipe[1] + ":p4";
|
||||||
|
int64_t res = std::stol(e->get_param_value_str("res"));
|
||||||
|
|
||||||
|
EXPECT_GT(res, 0);
|
||||||
|
EXPECT_LE(res, 2);
|
||||||
|
|
||||||
|
switch (res)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
EXPECT_EQ(expected_fds, fds)
|
||||||
|
<< "Value of fds is not one of expected values: " << fds;
|
||||||
|
;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
//
|
||||||
|
// On EC2 called from jenkins stdin returns POLLHUP
|
||||||
|
//
|
||||||
|
EXPECT_EQ(expected_fds, fds)
|
||||||
|
<< "Value of fds is not one of expected values: " << fds;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
FAIL();
|
||||||
|
}
|
||||||
|
|
||||||
|
callnum++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
|
||||||
|
EXPECT_EQ(2, callnum);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
TEST(inspector, invalid_file_name)
|
TEST(inspector, invalid_file_name)
|
||||||
{
|
{
|
||||||
sinsp inspector;
|
sinsp inspector;
|
||||||
|
@ -1665,6 +1752,123 @@ TEST_F(sys_call_test, failing_execve)
|
||||||
EXPECT_EQ(2, callnum);
|
EXPECT_EQ(2, callnum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(sys_call_test, large_execve)
|
||||||
|
{
|
||||||
|
const int buf_size = 100 * 1024;
|
||||||
|
const int driver_truncation_size = getpagesize();
|
||||||
|
const string non_existing_binary = "/non/existent";
|
||||||
|
const string existing_binary = "/bin/true";
|
||||||
|
|
||||||
|
int ctid;
|
||||||
|
int callnum = 0;
|
||||||
|
|
||||||
|
event_filter_t filter = [&](sinsp_evt* evt) { return evt->get_tid() == ctid; };
|
||||||
|
|
||||||
|
srandom(42);
|
||||||
|
|
||||||
|
string buf;
|
||||||
|
while (buf.length() < buf_size)
|
||||||
|
{
|
||||||
|
buf.append(std::to_string(random()));
|
||||||
|
}
|
||||||
|
|
||||||
|
run_callback_t test = [&](concurrent_object_handle<sinsp> inspector)
|
||||||
|
{
|
||||||
|
ctid = fork();
|
||||||
|
|
||||||
|
if (ctid < 0)
|
||||||
|
{
|
||||||
|
FAIL();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctid == 0)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
const char* eargv[] = {non_existing_binary.c_str(), buf.c_str(), NULL};
|
||||||
|
|
||||||
|
const char* eenvp[] = {buf.c_str(), NULL};
|
||||||
|
|
||||||
|
int ret = execve(eargv[0], (char* const*)eargv, (char* const*)eenvp);
|
||||||
|
ASSERT_TRUE(ret < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const char* eargv[] = {existing_binary.c_str(), buf.c_str(), NULL};
|
||||||
|
|
||||||
|
const char* eenvp[] = {buf.c_str(), NULL};
|
||||||
|
|
||||||
|
int ret = execve(eargv[0], (char* const*)eargv, (char* const*)eenvp);
|
||||||
|
ASSERT_TRUE(ret == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wait(NULL);
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
captured_event_callback_t callback = [&](const callback_param& param)
|
||||||
|
{
|
||||||
|
sinsp_evt* e = param.m_evt;
|
||||||
|
uint16_t type = e->get_type();
|
||||||
|
|
||||||
|
if (type == PPME_SYSCALL_EXECVE_19_E || type == PPME_SYSCALL_EXECVE_18_E)
|
||||||
|
{
|
||||||
|
++callnum;
|
||||||
|
|
||||||
|
string filename = e->get_param_value_str("filename");
|
||||||
|
|
||||||
|
if (callnum == 1)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(filename, non_existing_binary);
|
||||||
|
}
|
||||||
|
else if (callnum == 3)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(filename, existing_binary);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FAIL();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == PPME_SYSCALL_EXECVE_19_X || type == PPME_SYSCALL_EXECVE_18_X)
|
||||||
|
{
|
||||||
|
++callnum;
|
||||||
|
|
||||||
|
string exe = e->get_param_value_str("exe");
|
||||||
|
string args = e->get_param_value_str("args");
|
||||||
|
|
||||||
|
if (callnum == 2)
|
||||||
|
{
|
||||||
|
// This is the failed execve. exe and
|
||||||
|
// args will be available, but env
|
||||||
|
// will not.
|
||||||
|
EXPECT_EQ(exe, non_existing_binary.c_str());
|
||||||
|
EXPECT_EQ(args,
|
||||||
|
buf.substr(0, driver_truncation_size - non_existing_binary.length() - 2) + ".");
|
||||||
|
}
|
||||||
|
else if (callnum == 4)
|
||||||
|
{
|
||||||
|
string env = e->get_param_value_str("env");
|
||||||
|
|
||||||
|
EXPECT_EQ(exe, existing_binary);
|
||||||
|
EXPECT_EQ(
|
||||||
|
args,
|
||||||
|
buf.substr(0, driver_truncation_size - existing_binary.length() - 2) + ".");
|
||||||
|
EXPECT_EQ(env, buf.substr(0, driver_truncation_size - 1) + ".");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FAIL();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
|
||||||
|
EXPECT_EQ(4, callnum);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __x86_64__
|
#ifdef __x86_64__
|
||||||
|
|
||||||
TEST_F(sys_call_test32, failing_execve)
|
TEST_F(sys_call_test32, failing_execve)
|
||||||
|
@ -1771,4 +1975,537 @@ TEST_F(sys_call_test32, failing_execve)
|
||||||
EXPECT_EQ(10, callnum);
|
EXPECT_EQ(10, callnum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(sys_call_test32, mmap)
|
||||||
|
{
|
||||||
|
int callnum = 0;
|
||||||
|
int errno2;
|
||||||
|
|
||||||
|
proc_started_filter ps_filter;
|
||||||
|
|
||||||
|
//
|
||||||
|
// FILTER
|
||||||
|
//
|
||||||
|
event_filter_t filter = [&](sinsp_evt* evt)
|
||||||
|
{
|
||||||
|
auto tinfo = evt->get_thread_info(false);
|
||||||
|
return tinfo && tinfo->m_comm == "test_helper_32"
|
||||||
|
&& ps_filter(evt);
|
||||||
|
};
|
||||||
|
|
||||||
|
uint64_t p = 0;
|
||||||
|
|
||||||
|
//
|
||||||
|
// TEST CODE
|
||||||
|
//
|
||||||
|
run_callback_t test = [&](concurrent_object_handle<sinsp> inspector_handle)
|
||||||
|
{
|
||||||
|
subprocess handle(LIBSINSP_TEST_PATH "/test_helper_32",
|
||||||
|
{"mmap_test",});
|
||||||
|
std::stringstream tmp;
|
||||||
|
handle.out();
|
||||||
|
tmp << handle.out();
|
||||||
|
errno2 = std::stoi(tmp.str());
|
||||||
|
tmp.clear();
|
||||||
|
tmp.str("");
|
||||||
|
tmp << handle.out();
|
||||||
|
p = (uint64_t)std::stoul(tmp.str());
|
||||||
|
tmp.clear();
|
||||||
|
tmp.str("");
|
||||||
|
handle.wait();
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t enter_vmsize;
|
||||||
|
uint32_t enter_vmrss;
|
||||||
|
uint32_t exit_vmsize;
|
||||||
|
uint32_t exit_vmrss;
|
||||||
|
|
||||||
|
//
|
||||||
|
// OUTPUT VALDATION
|
||||||
|
//
|
||||||
|
captured_event_callback_t callback = [&](const callback_param& param)
|
||||||
|
{
|
||||||
|
sinsp_evt* e = param.m_evt;
|
||||||
|
uint16_t type = e->get_type();
|
||||||
|
|
||||||
|
if (type == PPME_SYSCALL_MUNMAP_E)
|
||||||
|
{
|
||||||
|
callnum++;
|
||||||
|
|
||||||
|
enter_vmsize = e->get_thread_info(false)->m_vmsize_kb;
|
||||||
|
enter_vmrss = e->get_thread_info(false)->m_vmrss_kb;
|
||||||
|
|
||||||
|
switch (callnum)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
EXPECT_EQ("50", e->get_param_value_str("addr"));
|
||||||
|
EXPECT_EQ("300", e->get_param_value_str("length"));
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
{
|
||||||
|
uint64_t addr = 0;
|
||||||
|
memcpy(&addr, e->get_param_by_name("addr")->m_val, sizeof(uint64_t));
|
||||||
|
#ifdef __LP64__
|
||||||
|
EXPECT_EQ((uint64_t)p, addr);
|
||||||
|
#else
|
||||||
|
EXPECT_EQ(((uint32_t)p), addr);
|
||||||
#endif
|
#endif
|
||||||
|
EXPECT_EQ("1003520", e->get_param_value_str("length"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
callnum--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == PPME_SYSCALL_MUNMAP_X)
|
||||||
|
{
|
||||||
|
callnum++;
|
||||||
|
|
||||||
|
memcpy(&exit_vmsize, e->get_param_by_name("vm_size")->m_val, sizeof(uint32_t));
|
||||||
|
memcpy(&exit_vmrss, e->get_param_by_name("vm_rss")->m_val, sizeof(uint32_t));
|
||||||
|
EXPECT_EQ(e->get_thread_info(false)->m_vmsize_kb, exit_vmsize);
|
||||||
|
EXPECT_EQ(e->get_thread_info(false)->m_vmrss_kb, exit_vmrss);
|
||||||
|
|
||||||
|
switch (callnum)
|
||||||
|
{
|
||||||
|
case 2:
|
||||||
|
EXPECT_EQ("EINVAL", e->get_param_value_str("res"));
|
||||||
|
EXPECT_EQ("-22", e->get_param_value_str("res", false));
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
EXPECT_EQ("0", e->get_param_value_str("res"));
|
||||||
|
EXPECT_GT(enter_vmsize, exit_vmsize + 500);
|
||||||
|
EXPECT_GE(enter_vmrss, enter_vmrss);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
callnum--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == PPME_SYSCALL_MMAP_E || type == PPME_SYSCALL_MMAP2_E)
|
||||||
|
{
|
||||||
|
callnum++;
|
||||||
|
|
||||||
|
enter_vmsize = e->get_thread_info(false)->m_vmsize_kb;
|
||||||
|
enter_vmrss = e->get_thread_info(false)->m_vmrss_kb;
|
||||||
|
|
||||||
|
switch (callnum)
|
||||||
|
{
|
||||||
|
case 3:
|
||||||
|
EXPECT_EQ("0", e->get_param_value_str("addr"));
|
||||||
|
EXPECT_EQ("0", e->get_param_value_str("length"));
|
||||||
|
EXPECT_EQ("PROT_READ|PROT_WRITE|PROT_EXEC", e->get_param_value_str("prot"));
|
||||||
|
EXPECT_EQ("MAP_SHARED|MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE",
|
||||||
|
e->get_param_value_str("flags"));
|
||||||
|
EXPECT_EQ("-1", e->get_param_value_str("fd", false));
|
||||||
|
|
||||||
|
if (type == PPME_SYSCALL_MMAP_E)
|
||||||
|
{
|
||||||
|
EXPECT_EQ("0", e->get_param_value_str("offset"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EXPECT_EQ("0", e->get_param_value_str("pgoffset"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
EXPECT_EQ("0", e->get_param_value_str("addr"));
|
||||||
|
EXPECT_EQ("1003520", e->get_param_value_str("length"));
|
||||||
|
EXPECT_EQ("PROT_READ|PROT_WRITE", e->get_param_value_str("prot"));
|
||||||
|
EXPECT_EQ("MAP_PRIVATE|MAP_ANONYMOUS", e->get_param_value_str("flags"));
|
||||||
|
EXPECT_EQ("-1", e->get_param_value_str("fd", false));
|
||||||
|
|
||||||
|
if (type == PPME_SYSCALL_MMAP_E)
|
||||||
|
{
|
||||||
|
EXPECT_EQ("0", e->get_param_value_str("offset"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EXPECT_EQ("0", e->get_param_value_str("pgoffset"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
callnum--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == PPME_SYSCALL_MMAP_X || type == PPME_SYSCALL_MMAP2_X)
|
||||||
|
{
|
||||||
|
callnum++;
|
||||||
|
|
||||||
|
memcpy(&exit_vmsize, e->get_param_by_name("vm_size")->m_val, sizeof(uint32_t));
|
||||||
|
memcpy(&exit_vmrss, e->get_param_by_name("vm_rss")->m_val, sizeof(uint32_t));
|
||||||
|
EXPECT_EQ(e->get_thread_info(false)->m_vmsize_kb, exit_vmsize);
|
||||||
|
EXPECT_EQ(e->get_thread_info(false)->m_vmrss_kb, exit_vmrss);
|
||||||
|
|
||||||
|
switch (callnum)
|
||||||
|
{
|
||||||
|
case 4:
|
||||||
|
{
|
||||||
|
uint64_t res = 0;
|
||||||
|
memcpy(&res, e->get_param_by_name("res")->m_val, sizeof(uint64_t));
|
||||||
|
EXPECT_EQ(-errno2, (int64_t)res);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 6:
|
||||||
|
{
|
||||||
|
uint64_t res = 0;
|
||||||
|
memcpy(&res, e->get_param_by_name("res")->m_val, sizeof(uint64_t));
|
||||||
|
EXPECT_EQ((uint64_t)p, res);
|
||||||
|
EXPECT_GT(exit_vmsize, enter_vmsize + 500);
|
||||||
|
EXPECT_GE(exit_vmrss, enter_vmrss);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
callnum--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
|
||||||
|
EXPECT_EQ(8, callnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(sys_call_test32, ppoll_timeout)
|
||||||
|
{
|
||||||
|
int callnum = 0;
|
||||||
|
event_filter_t filter = [&](sinsp_evt* evt)
|
||||||
|
{
|
||||||
|
auto tinfo = evt->get_thread_info(false);
|
||||||
|
return (evt->get_type() == PPME_SYSCALL_PPOLL_E ||
|
||||||
|
evt->get_type() == PPME_SYSCALL_PPOLL_X) &&
|
||||||
|
tinfo != nullptr && tinfo->m_comm == "test_helper_32";
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string my_pipe[2];
|
||||||
|
|
||||||
|
run_callback_t test = [&](concurrent_object_handle<sinsp> inspector)
|
||||||
|
{
|
||||||
|
subprocess handle(LIBSINSP_TEST_PATH "/test_helper_32",
|
||||||
|
{"ppoll_timeout",});
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << handle.out();
|
||||||
|
my_pipe[0] = ss.str();
|
||||||
|
ss.clear();
|
||||||
|
ss.str(" ");
|
||||||
|
ss << handle.out();
|
||||||
|
my_pipe[1] = ss.str();
|
||||||
|
ss.clear();
|
||||||
|
ss.str(" ");
|
||||||
|
handle.wait();
|
||||||
|
};
|
||||||
|
|
||||||
|
captured_event_callback_t callback = [&](const callback_param& param)
|
||||||
|
{
|
||||||
|
sinsp_evt* e = param.m_evt;
|
||||||
|
uint16_t type = e->get_type();
|
||||||
|
|
||||||
|
if (type == PPME_SYSCALL_PPOLL_E)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// stdin and stdout can be a file or a fifo depending
|
||||||
|
// on how the tests are invoked
|
||||||
|
//
|
||||||
|
std::string fds = e->get_param_value_str("fds");
|
||||||
|
std::string expected_fds = my_pipe[0] + ":p1 " +
|
||||||
|
my_pipe[1] + ":p4";
|
||||||
|
|
||||||
|
EXPECT_EQ(expected_fds, fds);
|
||||||
|
EXPECT_EQ("1000000", e->get_param_value_str("timeout", false));
|
||||||
|
EXPECT_EQ("SIGHUP SIGCHLD", e->get_param_value_str("sigmask", false));
|
||||||
|
callnum++;
|
||||||
|
}
|
||||||
|
else if (type == PPME_SYSCALL_PPOLL_X)
|
||||||
|
{
|
||||||
|
int64_t res = std::stol(e->get_param_value_str("res"));
|
||||||
|
|
||||||
|
EXPECT_GT(res, 0);
|
||||||
|
EXPECT_LE(res, 2);
|
||||||
|
|
||||||
|
string fds = e->get_param_value_str("fds");
|
||||||
|
std::string expected_fds = my_pipe[0] + ":p0 " +
|
||||||
|
my_pipe[1] + ":p4";
|
||||||
|
|
||||||
|
switch (res)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
EXPECT_EQ(expected_fds, fds);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
//
|
||||||
|
// On EC2 called from jenkins stdin returns POLLHUP
|
||||||
|
//
|
||||||
|
EXPECT_EQ(expected_fds, fds);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
FAIL();
|
||||||
|
}
|
||||||
|
|
||||||
|
callnum++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
|
||||||
|
EXPECT_EQ(2, callnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(sys_call_test32, fs_preadv)
|
||||||
|
{
|
||||||
|
int callnum = 0;
|
||||||
|
int fd = 0;
|
||||||
|
int fd1 = 0;
|
||||||
|
bool pwritev64_succeeded;
|
||||||
|
bool pwritev64_succeeded2;
|
||||||
|
proc_started_filter test_started_filter;
|
||||||
|
|
||||||
|
//
|
||||||
|
// FILTER
|
||||||
|
//
|
||||||
|
event_filter_t filter = [&](sinsp_evt* evt)
|
||||||
|
{
|
||||||
|
auto tinfo = evt->get_thread_info(false);
|
||||||
|
if (tinfo && tinfo->m_comm == "test_helper_32")
|
||||||
|
{
|
||||||
|
auto type = evt->get_type();
|
||||||
|
return (type == PPME_SYSCALL_PREADV_E
|
||||||
|
|| type == PPME_SYSCALL_PREADV_X
|
||||||
|
|| type == PPME_SYSCALL_PWRITEV_E
|
||||||
|
|| type == PPME_SYSCALL_PWRITEV_X);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// TEST CODE
|
||||||
|
//
|
||||||
|
run_callback_t test = [&](concurrent_object_handle<sinsp> inspector)
|
||||||
|
{
|
||||||
|
subprocess test_proc(LIBSINSP_TEST_PATH "/test_helper_32", {"preadv_pwritev"});
|
||||||
|
fd = std::stoi(test_proc.out());
|
||||||
|
int bool_n = std::stoi(test_proc.out());
|
||||||
|
pwritev64_succeeded = (bool_n == 1);
|
||||||
|
bool_n = std::stoi(test_proc.out());
|
||||||
|
pwritev64_succeeded2 = (bool_n == 1);
|
||||||
|
fd1 = std::stoi(test_proc.out());
|
||||||
|
test_proc.wait();
|
||||||
|
};
|
||||||
|
|
||||||
|
int pwrite1_res = 0;
|
||||||
|
int pwrite2_res = 0;
|
||||||
|
//
|
||||||
|
// OUTPUT VALDATION
|
||||||
|
//
|
||||||
|
captured_event_callback_t callback = [&](const callback_param& param)
|
||||||
|
{
|
||||||
|
sinsp_evt* e = param.m_evt;
|
||||||
|
uint16_t type = e->get_type();
|
||||||
|
|
||||||
|
if (type == PPME_SYSCALL_PWRITEV_E)
|
||||||
|
{
|
||||||
|
if (callnum == 0)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(fd, std::stoll(e->get_param_value_str("fd", false)));
|
||||||
|
EXPECT_EQ(987654321, std::stoll(e->get_param_value_str("pos")));
|
||||||
|
EXPECT_EQ(15, std::stoll(e->get_param_value_str("size")));
|
||||||
|
callnum++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EXPECT_EQ(fd, std::stoll(e->get_param_value_str("fd", false)));
|
||||||
|
EXPECT_EQ(10, std::stoll(e->get_param_value_str("pos")));
|
||||||
|
EXPECT_EQ(15, std::stoll(e->get_param_value_str("size")));
|
||||||
|
callnum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == PPME_SYSCALL_PWRITEV_X)
|
||||||
|
{
|
||||||
|
if (callnum == 1)
|
||||||
|
{
|
||||||
|
pwrite1_res = std::stoi(e->get_param_value_str("res", false));
|
||||||
|
EXPECT_EQ("aaaaabbbbbccccc", e->get_param_value_str("data"));
|
||||||
|
callnum++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pwrite2_res = std::stoi(e->get_param_value_str("res", false));
|
||||||
|
EXPECT_EQ("aaaaabbbbbccccc", e->get_param_value_str("data"));
|
||||||
|
callnum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == PPME_SYSCALL_PREADV_E)
|
||||||
|
{
|
||||||
|
if (callnum == 4)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(fd1, std::stoll(e->get_param_value_str("fd", false)));
|
||||||
|
EXPECT_EQ(987654321, std::stoll(e->get_param_value_str("pos")));
|
||||||
|
callnum++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EXPECT_EQ(fd1, std::stoll(e->get_param_value_str("fd", false)));
|
||||||
|
EXPECT_EQ(10, std::stoll(e->get_param_value_str("pos")));
|
||||||
|
callnum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == PPME_SYSCALL_PREADV_X)
|
||||||
|
{
|
||||||
|
if (callnum == 3)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(15, std::stoi(e->get_param_value_str("res", false)));
|
||||||
|
EXPECT_EQ("aaaaabbbbb", e->get_param_value_str("data"));
|
||||||
|
EXPECT_EQ(30, std::stoll(e->get_param_value_str("size")));
|
||||||
|
callnum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
|
||||||
|
EXPECT_EQ(6, callnum);
|
||||||
|
if (pwritev64_succeeded)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(15, pwrite1_res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EXPECT_GT(0, pwrite1_res);
|
||||||
|
}
|
||||||
|
if (pwritev64_succeeded2)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(15, pwrite2_res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EXPECT_EQ(-22, pwrite2_res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
int32_t scap_proc_read_thread(struct scap_linux_platform* linux_platform,
|
||||||
|
char* procdirname,
|
||||||
|
uint64_t tid,
|
||||||
|
struct scap_threadinfo* tinfo,
|
||||||
|
char* error,
|
||||||
|
bool scan_sockets);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(sys_call_test, thread_lookup_static)
|
||||||
|
{
|
||||||
|
char err_buf[SCAP_LASTERR_SIZE];
|
||||||
|
scap_threadinfo scap_tinfo;
|
||||||
|
char proc[] = LIBSINSP_TEST_RESOURCES_PATH "/_proc";
|
||||||
|
struct stat s = {};
|
||||||
|
if (stat(proc, &s) != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s not found, skipping test\n", proc);
|
||||||
|
FAIL();
|
||||||
|
}
|
||||||
|
|
||||||
|
event_filter_t filter = [&](sinsp_evt* evt)
|
||||||
|
{ return evt->get_type() != PPME_PROCEXIT_1_E && evt->get_tid() > 0; };
|
||||||
|
run_callback_t test = [&](concurrent_object_handle<sinsp> inspector) {return;};
|
||||||
|
captured_event_callback_t callback = [&](const callback_param& param) {return;};
|
||||||
|
scap_linux_platform *platform;
|
||||||
|
|
||||||
|
before_close_t before_close = [&](sinsp* inspector)
|
||||||
|
{
|
||||||
|
platform = (scap_linux_platform*)inspector->get_scap_platform();
|
||||||
|
};
|
||||||
|
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
{ event_capture::run(test, callback, filter, event_capture::do_nothing, before_close); });
|
||||||
|
|
||||||
|
ASSERT_EQ(SCAP_SUCCESS,
|
||||||
|
scap_proc_read_thread(platform, proc, 1, &scap_tinfo, err_buf, false));
|
||||||
|
|
||||||
|
EXPECT_EQ(1, scap_tinfo.tid);
|
||||||
|
EXPECT_EQ(1, scap_tinfo.pid);
|
||||||
|
EXPECT_EQ(1, scap_tinfo.vtid);
|
||||||
|
EXPECT_EQ(0, scap_tinfo.ptid);
|
||||||
|
|
||||||
|
ASSERT_EQ(SCAP_SUCCESS,
|
||||||
|
scap_proc_read_thread(platform, proc, 62725, &scap_tinfo, err_buf, false));
|
||||||
|
EXPECT_EQ(62725, scap_tinfo.tid);
|
||||||
|
EXPECT_EQ(62725, scap_tinfo.pid);
|
||||||
|
EXPECT_EQ(62725, scap_tinfo.vtid);
|
||||||
|
EXPECT_EQ(1, scap_tinfo.ptid);
|
||||||
|
|
||||||
|
ASSERT_EQ(SCAP_SUCCESS,
|
||||||
|
scap_proc_read_thread(platform, proc, 62727, &scap_tinfo, err_buf, false));
|
||||||
|
EXPECT_EQ(62727, scap_tinfo.tid);
|
||||||
|
EXPECT_EQ(62725, scap_tinfo.pid);
|
||||||
|
EXPECT_EQ(62727, scap_tinfo.vtid);
|
||||||
|
EXPECT_EQ(1, scap_tinfo.ptid);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(sys_call_test, thread_lookup_live)
|
||||||
|
{
|
||||||
|
char err_buf[SCAP_LASTERR_SIZE];
|
||||||
|
scap_threadinfo scap_tinfo;
|
||||||
|
char proc[] = "/proc";
|
||||||
|
|
||||||
|
std::unordered_set<int64_t> seen_tids;
|
||||||
|
|
||||||
|
event_filter_t filter = [&](sinsp_evt* evt)
|
||||||
|
{ return evt->get_type() != PPME_PROCEXIT_1_E && evt->get_tid() > 0; };
|
||||||
|
run_callback_t test = [&](concurrent_object_handle<sinsp> inspector)
|
||||||
|
{
|
||||||
|
// a very short sleep to gather some events,
|
||||||
|
// we'll take much longer than this to process them all
|
||||||
|
usleep(1000);
|
||||||
|
};
|
||||||
|
captured_event_callback_t callback = [&](const callback_param& param)
|
||||||
|
{
|
||||||
|
sinsp_evt* e = param.m_evt;
|
||||||
|
auto tid = e->get_tid();
|
||||||
|
if (!seen_tids.insert(tid).second)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "looking up tid %ld in /proc\n", tid);
|
||||||
|
// In some cases scap_proc_read_thread can return SCAP_SUCCESS without
|
||||||
|
// filling in scap_tinfo
|
||||||
|
if (scap_proc_read_thread((scap_linux_platform*)param.m_inspector->get_scap_platform(),
|
||||||
|
proc, tid, &scap_tinfo, err_buf, false) == SCAP_SUCCESS)
|
||||||
|
{
|
||||||
|
auto tinfo = e->get_thread_info(false);
|
||||||
|
if (!tinfo)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EXPECT_NE(0, scap_tinfo.tid);
|
||||||
|
EXPECT_NE(0, scap_tinfo.pid);
|
||||||
|
EXPECT_NE(0, scap_tinfo.vtid);
|
||||||
|
EXPECT_EQ(tinfo->m_tid, scap_tinfo.tid);
|
||||||
|
EXPECT_EQ(tinfo->m_pid, scap_tinfo.pid);
|
||||||
|
EXPECT_EQ(tinfo->m_vtid, scap_tinfo.vtid);
|
||||||
|
// Not testing scap_tinfo.ptid because it can change in between event and lookup
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
scap_linux_platform *platform;
|
||||||
|
|
||||||
|
before_close_t before_close = [&](sinsp* inspector)
|
||||||
|
{
|
||||||
|
// close scap to maintain the num_consumers at exit == 0 assertion
|
||||||
|
//close_capture(scap, platform);
|
||||||
|
platform = (scap_linux_platform*)inspector->get_scap_platform();
|
||||||
|
};
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
{ event_capture::run(test, callback, filter, event_capture::do_nothing, before_close); });
|
||||||
|
|
||||||
|
ASSERT_EQ(SCAP_SUCCESS,
|
||||||
|
scap_proc_read_thread(platform, proc, getpid(),
|
||||||
|
&scap_tinfo, err_buf, false));
|
||||||
|
EXPECT_EQ(getpid(), scap_tinfo.tid);
|
||||||
|
EXPECT_EQ(getpid(), scap_tinfo.pid);
|
||||||
|
EXPECT_EQ(getpid(), scap_tinfo.vtid);
|
||||||
|
EXPECT_EQ(getppid(), scap_tinfo.ptid);
|
||||||
|
|
||||||
|
ASSERT_EQ(SCAP_SUCCESS,
|
||||||
|
scap_proc_read_thread(platform, proc, 1,
|
||||||
|
&scap_tinfo, err_buf, false));
|
||||||
|
EXPECT_EQ(1, scap_tinfo.tid);
|
||||||
|
EXPECT_EQ(1, scap_tinfo.pid);
|
||||||
|
EXPECT_EQ(1, scap_tinfo.vtid);
|
||||||
|
EXPECT_EQ(0, scap_tinfo.ptid);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -159,8 +159,7 @@ void runtest(iotype iot,
|
||||||
if (evt->get_fd_info()->m_type != SCAP_FD_IPV4_SOCK)
|
if (evt->get_fd_info()->m_type != SCAP_FD_IPV4_SOCK)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// Skip non-tcp sockets. Python opens unix sockets
|
// Skip non-tcp sockets. Python opens unix sockets.
|
||||||
// to god knows what.
|
|
||||||
//
|
//
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -308,7 +307,10 @@ void runtest(iotype iot,
|
||||||
//
|
//
|
||||||
// OUTPUT VALDATION
|
// OUTPUT VALDATION
|
||||||
//
|
//
|
||||||
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
|
ASSERT_NO_FATAL_FAILURE({event_capture::run(test, callback, filter, event_capture::do_nothing,
|
||||||
|
event_capture::do_nothing, event_capture::always_continue, 131072,
|
||||||
|
(uint64_t)60 * 1000 * 1000 * 1000, (uint64_t)60 * 1000 * 1000 * 1000,
|
||||||
|
SINSP_MODE_LIVE, 3, false); });
|
||||||
ASSERT_GT(callnum,0);
|
ASSERT_GT(callnum,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,7 +408,10 @@ TEST_F(sys_call_test, tcp_client_server_with_connection_before_capturing_starts)
|
||||||
server.wait_till_ready();
|
server.wait_till_ready();
|
||||||
client.wait_till_ready();
|
client.wait_till_ready();
|
||||||
|
|
||||||
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
|
ASSERT_NO_FATAL_FAILURE({event_capture::run(test, callback, filter, event_capture::do_nothing,
|
||||||
|
event_capture::do_nothing, event_capture::always_continue, 131072,
|
||||||
|
(uint64_t)60 * 1000 * 1000 * 1000, (uint64_t)60 * 1000 * 1000 * 1000,
|
||||||
|
SINSP_MODE_LIVE, 3, false); });
|
||||||
ASSERT_EQ(1, state);
|
ASSERT_EQ(1, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -670,7 +670,10 @@ void runtest_ipv4m(iotype iot,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
|
ASSERT_NO_FATAL_FAILURE({event_capture::run(test, callback, filter, event_capture::do_nothing,
|
||||||
|
event_capture::do_nothing, event_capture::always_continue, 131072,
|
||||||
|
(uint64_t)60 * 1000 * 1000 * 1000, (uint64_t)60 * 1000 * 1000 * 1000,
|
||||||
|
SINSP_MODE_LIVE, 3, false); });
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(sys_call_test, tcp_client_server_ipv4m)
|
TEST_F(sys_call_test, tcp_client_server_ipv4m)
|
||||||
|
|
|
@ -43,6 +43,7 @@ limitations under the License.
|
||||||
|
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
#include <sys/quota.h>
|
#include <sys/quota.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
@ -68,6 +69,10 @@ void mmap_test(const vector<string>& args)
|
||||||
{
|
{
|
||||||
int errno2;
|
int errno2;
|
||||||
void* p;
|
void* p;
|
||||||
|
|
||||||
|
printf("STARTED\n");
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
munmap((void*)0x50, 300);
|
munmap((void*)0x50, 300);
|
||||||
p = mmap(0,
|
p = mmap(0,
|
||||||
0,
|
0,
|
||||||
|
@ -78,7 +83,10 @@ void mmap_test(const vector<string>& args)
|
||||||
errno2 = errno;
|
errno2 = errno;
|
||||||
p = mmap(NULL, 1003520, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
p = mmap(NULL, 1003520, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
munmap(p, 1003520);
|
munmap(p, 1003520);
|
||||||
cout << errno2 << " " << p << endl;
|
printf("%d\n", errno2);
|
||||||
|
fflush(stdout);
|
||||||
|
printf("%u\n", p);
|
||||||
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool str_to_bool(const string& s)
|
bool str_to_bool(const string& s)
|
||||||
|
@ -182,6 +190,8 @@ void preadv_pwritev(const vector<string>& args)
|
||||||
//
|
//
|
||||||
bool pwritev64_succeeded = bytes_sent > 0;
|
bool pwritev64_succeeded = bytes_sent > 0;
|
||||||
|
|
||||||
|
cout << fd << endl;
|
||||||
|
|
||||||
cout << (pwritev64_succeeded ? 1 : 0) << endl;
|
cout << (pwritev64_succeeded ? 1 : 0) << endl;
|
||||||
|
|
||||||
bytes_sent = pwritev(fd, wv, wv_count, 10);
|
bytes_sent = pwritev(fd, wv, wv_count, 10);
|
||||||
|
@ -192,6 +202,8 @@ void preadv_pwritev(const vector<string>& args)
|
||||||
|
|
||||||
auto fd1 = open(FILENAME, O_CREAT | O_RDONLY, S_IRWXU);
|
auto fd1 = open(FILENAME, O_CREAT | O_RDONLY, S_IRWXU);
|
||||||
|
|
||||||
|
cout << fd1 << endl;
|
||||||
|
|
||||||
wv[0].iov_len = sizeof(msg1);
|
wv[0].iov_len = sizeof(msg1);
|
||||||
wv[1].iov_len = sizeof(msg2);
|
wv[1].iov_len = sizeof(msg2);
|
||||||
wv[2].iov_len = sizeof(msg3);
|
wv[2].iov_len = sizeof(msg3);
|
||||||
|
@ -223,11 +235,12 @@ void quotactl_ok(const vector<string>& args)
|
||||||
{
|
{
|
||||||
struct dqblk mydqblk;
|
struct dqblk mydqblk;
|
||||||
struct dqinfo mydqinfo;
|
struct dqinfo mydqinfo;
|
||||||
|
std::string caddr = args[0] + "/aquota.user";
|
||||||
quotactl(QCMD(Q_QUOTAON, USRQUOTA),
|
quotactl(QCMD(Q_QUOTAON, USRQUOTA),
|
||||||
"/dev/loop0",
|
args[1].c_str(),
|
||||||
2,
|
2,
|
||||||
(caddr_t) "/tmp/testquotamnt/aquota.user"); // 2 => QFMT_VFS_V0
|
(caddr_t)caddr.c_str()); // 2 => QFMT_VFS_V0
|
||||||
quotactl(QCMD(Q_GETQUOTA, USRQUOTA), "/dev/loop0", 0, (caddr_t)&mydqblk); // 0 => root user
|
quotactl(QCMD(Q_GETQUOTA, USRQUOTA), args[1].c_str(), 0, (caddr_t)&mydqblk); // 0 => root user
|
||||||
fwrite(&mydqblk.dqb_bhardlimit, 1, sizeof(uint64_t), stdout);
|
fwrite(&mydqblk.dqb_bhardlimit, 1, sizeof(uint64_t), stdout);
|
||||||
fwrite(&mydqblk.dqb_bsoftlimit, 1, sizeof(uint64_t), stdout);
|
fwrite(&mydqblk.dqb_bsoftlimit, 1, sizeof(uint64_t), stdout);
|
||||||
fwrite(&mydqblk.dqb_curspace, 1, sizeof(uint64_t), stdout);
|
fwrite(&mydqblk.dqb_curspace, 1, sizeof(uint64_t), stdout);
|
||||||
|
@ -235,10 +248,33 @@ void quotactl_ok(const vector<string>& args)
|
||||||
fwrite(&mydqblk.dqb_isoftlimit, 1, sizeof(uint64_t), stdout);
|
fwrite(&mydqblk.dqb_isoftlimit, 1, sizeof(uint64_t), stdout);
|
||||||
fwrite(&mydqblk.dqb_btime, 1, sizeof(uint64_t), stdout);
|
fwrite(&mydqblk.dqb_btime, 1, sizeof(uint64_t), stdout);
|
||||||
fwrite(&mydqblk.dqb_itime, 1, sizeof(uint64_t), stdout);
|
fwrite(&mydqblk.dqb_itime, 1, sizeof(uint64_t), stdout);
|
||||||
quotactl(QCMD(Q_GETINFO, USRQUOTA), "/dev/loop0", 0, (caddr_t)&mydqinfo);
|
quotactl(QCMD(Q_GETINFO, USRQUOTA), args[1].c_str(), 0, (caddr_t)&mydqinfo);
|
||||||
fwrite(&mydqinfo.dqi_bgrace, 1, sizeof(uint64_t), stdout);
|
fwrite(&mydqinfo.dqi_bgrace, 1, sizeof(uint64_t), stdout);
|
||||||
fwrite(&mydqinfo.dqi_igrace, 1, sizeof(uint64_t), stdout);
|
fwrite(&mydqinfo.dqi_igrace, 1, sizeof(uint64_t), stdout);
|
||||||
quotactl(QCMD(Q_QUOTAOFF, USRQUOTA), "/dev/loop0", 0, NULL);
|
quotactl(QCMD(Q_QUOTAOFF, USRQUOTA), args[1].c_str(), 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void poll_timeout(const vector<string>& args)
|
||||||
|
{
|
||||||
|
int my_pipe[2];
|
||||||
|
auto ret = pipe(my_pipe);
|
||||||
|
if (ret != 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pollfd ufds[2];
|
||||||
|
ufds[0].fd = my_pipe[0];
|
||||||
|
ufds[0].events = POLLIN;
|
||||||
|
ufds[1].fd = my_pipe[1];
|
||||||
|
ufds[1].events = POLLOUT;
|
||||||
|
|
||||||
|
poll(ufds, 2, 20);
|
||||||
|
|
||||||
|
printf("%d\n", my_pipe[0]);
|
||||||
|
fflush(stdout);
|
||||||
|
printf("%d\n", my_pipe[1]);
|
||||||
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ppoll_timeout(const vector<string>& args)
|
void ppoll_timeout(const vector<string>& args)
|
||||||
|
@ -265,6 +301,11 @@ void ppoll_timeout(const vector<string>& args)
|
||||||
sigaddset(&sigs, SIGHUP);
|
sigaddset(&sigs, SIGHUP);
|
||||||
sigaddset(&sigs, SIGCHLD);
|
sigaddset(&sigs, SIGCHLD);
|
||||||
ppoll(ufds, 2, &timeout, &sigs);
|
ppoll(ufds, 2, &timeout, &sigs);
|
||||||
|
|
||||||
|
printf("%d\n", my_pipe[0]);
|
||||||
|
fflush(stdout);
|
||||||
|
printf("%d\n", my_pipe[1]);
|
||||||
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pgid_test(const vector<string>& args)
|
void pgid_test(const vector<string>& args)
|
||||||
|
@ -710,6 +751,7 @@ const unordered_map<string, function<void(const vector<string>&)>> func_map = {
|
||||||
{"preadv_pwritev", preadv_pwritev},
|
{"preadv_pwritev", preadv_pwritev},
|
||||||
{"quotactl_ko", quotactl_ko},
|
{"quotactl_ko", quotactl_ko},
|
||||||
{"quotactl_ok", quotactl_ok},
|
{"quotactl_ok", quotactl_ok},
|
||||||
|
{"poll_timeout", poll_timeout},
|
||||||
{"ppoll_timeout", ppoll_timeout},
|
{"ppoll_timeout", ppoll_timeout},
|
||||||
{"pgid_test", pgid_test},
|
{"pgid_test", pgid_test},
|
||||||
{"custom_container", custom_container},
|
{"custom_container", custom_container},
|
||||||
|
|
|
@ -0,0 +1,278 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
/*
|
||||||
|
Copyright (C) 2024 The Falco Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License 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.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "event_capture.h"
|
||||||
|
#include "subprocess.h"
|
||||||
|
#include "sys_call_test.h"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <libsinsp/event.h>
|
||||||
|
#include <libsinsp/sinsp.h>
|
||||||
|
#include <libsinsp/sinsp_int.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <poll.h>
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <list>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
#include <libsinsp/sinsp_int.h>
|
||||||
|
|
||||||
|
#define NAME "/tmp/python_unix_sockets_example"
|
||||||
|
#define PAYLOAD "0123456789QWERTYUIOPASDFGHJKLZXCVBNM"
|
||||||
|
|
||||||
|
#define PAYLOAD "0123456789QWERTYUIOPASDFGHJKLZXCVBNM"
|
||||||
|
#define BUFFER_LENGTH (sizeof(PAYLOAD) - 1)
|
||||||
|
#define FALSE 0
|
||||||
|
|
||||||
|
inline void parse_tuple(const std::string& tuple,
|
||||||
|
std::string& srcstr,
|
||||||
|
std::string& dststr, bool shift = false)
|
||||||
|
{
|
||||||
|
std::string token;
|
||||||
|
std::stringstream ss(tuple);
|
||||||
|
std::vector<std::string> tst;
|
||||||
|
|
||||||
|
int base = shift? 1 : 0;
|
||||||
|
|
||||||
|
while (std::getline(ss, token, '>')) {
|
||||||
|
tst.push_back(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
int size = shift? 3 : 2;
|
||||||
|
EXPECT_EQ(size, (int)tst.size());
|
||||||
|
|
||||||
|
srcstr = tst[base].substr(0, tst[base].size() - 1);
|
||||||
|
dststr = tst[base+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool ends_with(const std::string& value, const std::string& ending)
|
||||||
|
{
|
||||||
|
if (ending.size() > value.size()) return false;
|
||||||
|
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(sys_call_test, unix_client_server)
|
||||||
|
{
|
||||||
|
int32_t callnum = 0;
|
||||||
|
bool first_connect_or_accept_seen = true;
|
||||||
|
std::string sport;
|
||||||
|
std::string src_addr;
|
||||||
|
std::string dest_addr;
|
||||||
|
|
||||||
|
//
|
||||||
|
// FILTER
|
||||||
|
//
|
||||||
|
event_filter_t filter = [&](sinsp_evt* evt)
|
||||||
|
{
|
||||||
|
sinsp_threadinfo* ti = evt->get_thread_info(false);
|
||||||
|
if (ti)
|
||||||
|
{
|
||||||
|
if (ti->get_comm() == "python2" && ti->m_args.size() >= 1)
|
||||||
|
{
|
||||||
|
return ends_with(ti->m_args[0],"unix_client_server.py") ||
|
||||||
|
ends_with(ti->m_args[0],"unix_client_server.py");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// INITIALIZATION
|
||||||
|
//
|
||||||
|
run_callback_t test = [](concurrent_object_handle<sinsp> inspector)
|
||||||
|
{
|
||||||
|
subprocess server("python2", {LIBSINSP_TEST_RESOURCES_PATH "/unix_client_server.py", "server"});
|
||||||
|
|
||||||
|
server.wait_for_start();
|
||||||
|
|
||||||
|
subprocess client("python2", {LIBSINSP_TEST_RESOURCES_PATH "/unix_client_server.py", "client"});
|
||||||
|
server.wait();
|
||||||
|
client.wait();
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// OUTPUT VALIDATION
|
||||||
|
//
|
||||||
|
captured_event_callback_t callback = [&](const callback_param& param)
|
||||||
|
{
|
||||||
|
sinsp_evt* evt = param.m_evt;
|
||||||
|
|
||||||
|
//std::cout << evt->get_name() << std::endl;
|
||||||
|
|
||||||
|
if (evt->get_type() == PPME_SOCKET_CONNECT_X)
|
||||||
|
{
|
||||||
|
std::string tuple = evt->get_param_value_str("tuple");
|
||||||
|
std::string addrs = tuple.substr(0, tuple.find(" "));
|
||||||
|
std::string file = tuple.substr(tuple.find(" ") + 1);
|
||||||
|
|
||||||
|
EXPECT_EQ(NAME, file);
|
||||||
|
|
||||||
|
std::string srcstr;
|
||||||
|
std::string dststr;
|
||||||
|
parse_tuple(tuple, srcstr, dststr);
|
||||||
|
|
||||||
|
EXPECT_NE("0000000000000000", srcstr);
|
||||||
|
EXPECT_NE("0000000000000000", dststr);
|
||||||
|
|
||||||
|
//
|
||||||
|
// connect() and accept() can return
|
||||||
|
// in a different order
|
||||||
|
//
|
||||||
|
if (first_connect_or_accept_seen)
|
||||||
|
{
|
||||||
|
first_connect_or_accept_seen = false;
|
||||||
|
src_addr = srcstr.substr(1);
|
||||||
|
dest_addr = dststr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EXPECT_EQ(src_addr, srcstr.substr(1));
|
||||||
|
EXPECT_EQ(dest_addr, dststr);
|
||||||
|
}
|
||||||
|
|
||||||
|
callnum++;
|
||||||
|
}
|
||||||
|
else if ((evt->get_type() == PPME_SOCKET_ACCEPT_5_X) ||
|
||||||
|
(evt->get_type() == PPME_SOCKET_ACCEPT4_6_X))
|
||||||
|
{
|
||||||
|
std::string tuple = evt->get_param_value_str("tuple");
|
||||||
|
std::string addrs = tuple.substr(0, tuple.find(" "));
|
||||||
|
std::string file = tuple.substr(tuple.find(" ") + 1);
|
||||||
|
|
||||||
|
EXPECT_EQ(NAME, file);
|
||||||
|
|
||||||
|
std::string srcstr;
|
||||||
|
std::string dststr;
|
||||||
|
parse_tuple(tuple, srcstr, dststr);
|
||||||
|
|
||||||
|
EXPECT_NE("0000000000000000", srcstr);
|
||||||
|
EXPECT_NE("0000000000000000", dststr);
|
||||||
|
|
||||||
|
//
|
||||||
|
// connect() and accept() can return
|
||||||
|
// in a different order
|
||||||
|
//
|
||||||
|
if (first_connect_or_accept_seen)
|
||||||
|
{
|
||||||
|
first_connect_or_accept_seen = false;
|
||||||
|
src_addr = srcstr.substr(1);
|
||||||
|
dest_addr = dststr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EXPECT_EQ(src_addr, srcstr.substr(1));
|
||||||
|
EXPECT_EQ(dest_addr, dststr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string fdtuple = evt->get_param_value_str("tuple");
|
||||||
|
std::string fdaddrs = fdtuple.substr(0, fdtuple.find(" "));
|
||||||
|
std::string fdfile = fdtuple.substr(fdtuple.find(" ") + 1);
|
||||||
|
|
||||||
|
EXPECT_EQ(NAME, fdfile);
|
||||||
|
|
||||||
|
std::string fdsrcstr;
|
||||||
|
std::string fddststr;
|
||||||
|
parse_tuple(tuple, fdsrcstr, fddststr);
|
||||||
|
|
||||||
|
EXPECT_NE("0000000000000000", fdsrcstr);
|
||||||
|
EXPECT_NE("0000000000000000", fddststr);
|
||||||
|
|
||||||
|
callnum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callnum < 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 32bit (and s390x) uses send() and recv(), while 64bit
|
||||||
|
// uses sendto() and recvfrom() and sets the address to NULL
|
||||||
|
//
|
||||||
|
if (evt->get_type() == PPME_SOCKET_SEND_E || evt->get_type() == PPME_SOCKET_RECV_E ||
|
||||||
|
evt->get_type() == PPME_SOCKET_SENDTO_E || evt->get_type() == PPME_SOCKET_RECVFROM_E)
|
||||||
|
{
|
||||||
|
if (((evt->get_type() == PPME_SOCKET_RECVFROM_X) ||
|
||||||
|
(evt->get_type() == PPME_SOCKET_RECVFROM_X)) &&
|
||||||
|
(evt->get_param_value_str("tuple") != ""))
|
||||||
|
{
|
||||||
|
EXPECT_EQ("NULL", evt->get_param_value_str("tuple"));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string fdtuple = evt->get_param_value_str("fd");
|
||||||
|
std::string fdaddrs = fdtuple.substr(0, fdtuple.find(" "));
|
||||||
|
std::string fdfile = fdtuple.substr(fdtuple.find(" ") + 1);
|
||||||
|
|
||||||
|
EXPECT_EQ(NAME, fdfile);
|
||||||
|
|
||||||
|
std::string fdsrcstr;
|
||||||
|
std::string fddststr;
|
||||||
|
parse_tuple(fdtuple, fdsrcstr, fddststr, true);
|
||||||
|
|
||||||
|
EXPECT_NE("0", fdsrcstr);
|
||||||
|
EXPECT_NE("0", fddststr);
|
||||||
|
|
||||||
|
callnum++;
|
||||||
|
}
|
||||||
|
else if ((evt->get_type() == PPME_SOCKET_RECV_X) ||
|
||||||
|
(evt->get_type() == PPME_SOCKET_RECVFROM_X))
|
||||||
|
{
|
||||||
|
if (evt->get_type() == PPME_SOCKET_RECVFROM_X)
|
||||||
|
{
|
||||||
|
if (callnum == 5)
|
||||||
|
{
|
||||||
|
std::string tuple = evt->get_param_value_str("tuple");
|
||||||
|
std::string addrs = tuple.substr(0, tuple.find(" "));
|
||||||
|
std::string file = tuple.substr(tuple.find(" ") + 1);
|
||||||
|
|
||||||
|
EXPECT_EQ(NAME, file);
|
||||||
|
|
||||||
|
std::string srcstr;
|
||||||
|
std::string dststr;
|
||||||
|
parse_tuple(tuple, srcstr, dststr);
|
||||||
|
|
||||||
|
EXPECT_NE("0000000000000000", srcstr);
|
||||||
|
EXPECT_NE("0000000000000000", dststr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPECT_EQ(PAYLOAD, evt->get_param_value_str("data"));
|
||||||
|
|
||||||
|
callnum++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// OUTPUT VALDATION
|
||||||
|
//
|
||||||
|
ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); });
|
||||||
|
EXPECT_FALSE(first_connect_or_accept_seen);
|
||||||
|
EXPECT_EQ(8, callnum);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue