Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Parse incomplete chunks 9/9] Add bpf test for loop limit exceeded #1794

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Add bpf test for loop limit exceeded
Signed-off-by: Benjamin Kilimnik <[email protected]>
  • Loading branch information
benkilimnik committed Nov 29, 2023
commit e0c8068941aad68512a291436683f3d5bdb0f02f
21 changes: 21 additions & 0 deletions src/stirling/source_connectors/socket_tracer/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 474,27 @@ pl_cc_bpf_test(
],
)

pl_cc_bpf_test(
name = "nodejs_trace_bpf_test",
timeout = "long",
srcs = ["nodejs_trace_bpf_test.cc"],
flaky = True,
shard_count = 2,
tags = [
"cpu:16",
"no_asan",
"requires_bpf",
],
deps = [
":cc_library",
"//src/common/testing/test_utils:cc_library",
"//src/stirling/source_connectors/socket_tracer/testing:cc_library",
"//src/stirling/source_connectors/socket_tracer/testing/container_images:acorn_node14_container",
"//src/stirling/source_connectors/socket_tracer/testing/container_images:curl_container",
"//src/stirling/testing:cc_library",
],
)

pl_cc_bpf_test(
name = "boringssl_trace_bpf_test",
timeout = "long",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 1,165 @@
/*
* Copyright 2018- The Pixie 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.
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <string>

#include <prometheus/text_serializer.h>
#include "src/common/base/base.h"
#include "src/common/exec/exec.h"
#include "src/common/testing/test_environment.h"
#include "src/shared/types/column_wrapper.h"
#include "src/shared/types/types.h"
#include "src/stirling/source_connectors/socket_tracer/socket_trace_connector.h"
#include "src/stirling/source_connectors/socket_tracer/testing/container_images/curl_container.h"
#include "src/stirling/source_connectors/socket_tracer/testing/container_images/acorn_node14_container.h"
#include "src/stirling/source_connectors/socket_tracer/testing/protocol_checkers.h"
#include "src/stirling/source_connectors/socket_tracer/testing/socket_trace_bpf_test_fixture.h"
#include "src/stirling/source_connectors/socket_tracer/uprobe_symaddrs.h"
#include "src/stirling/testing/common.h"

namespace px {
namespace stirling {

namespace http = protocols::http;

using ::px::stirling::testing::EqHTTPRecord;
using ::px::stirling::testing::EqHTTPRecordWithBodyRegex;
using ::px::stirling::testing::FindRecordIdxMatchesPID;
using ::px::stirling::testing::GetTargetRecords;
using ::px::stirling::testing::SocketTraceBPFTestFixture;
using ::px::stirling::testing::ToRecordVector;

using ::testing::StrEq;
using ::testing::Types;
using ::testing::UnorderedElementsAre;

class AcornNode14ContainerWrapper : public ::px::stirling::testing::AcornNode14Container {
public:
int32_t PID() const { return process_pid(); }
};

// Includes all information we need to extract from the trace records, which are used to verify
// against the expected results.
struct TraceRecords {
std::vector<http::Record> http_records;
std::vector<std::string> remote_address;
};

template <typename TServerContainer>
class NodeJSTraceTest : public SocketTraceBPFTestFixture</* TClientSideTracing */ false> {
protected:
NodeJSTraceTest() {
// The container runner will make sure it is in the ready state before unblocking.
// Stirling will run after this unblocks, as part of SocketTraceBPFTest SetUp().
StatusOr<std::string> run_result = server_.Run(std::chrono::seconds{60});
PX_CHECK_OK(run_result);

// Sleep an additional second, just to be safe.
sleep(1);
}

// Returns the trace records of the process specified by the input pid.
TraceRecords GetTraceRecords(int pid) {
std::vector<TaggedRecordBatch> tablets =
this->ConsumeRecords(SocketTraceConnector::kHTTPTableNum);
if (tablets.empty()) {
return {};
}
types::ColumnWrapperRecordBatch record_batch = tablets[0].records;
std::vector<size_t> server_record_indices =
FindRecordIdxMatchesPID(record_batch, kHTTPUPIDIdx, pid);
std::vector<http::Record> http_records =
ToRecordVector<http::Record>(record_batch, server_record_indices);
std::vector<std::string> remote_addresses =
testing::GetRemoteAddrs(record_batch, server_record_indices);
return {std::move(http_records), std::move(remote_addresses)};
}

TServerContainer server_;
};

//-----------------------------------------------------------------------------
// Test Scenarios
//-----------------------------------------------------------------------------

std::string_view kPartialRespBody = R"delimiter(
295885c4 200 4096 8eee8a560fc1b68e046e9cb5de9a4c64b146667c93280582f5105914c333144

8eee8a560fc1b68e046e9cb5de9a4c64b146667c93280582f5105914c333144
8eee8a560fc1b68e046e9cb5de9a4c64b146667c93280582f5105914c333144
8eee8a560fc1b68e046e9cb5de9a4c64b146667c93280582f5105914c333144
8eee8a560fc1b68e046e9cb5de9a4c64b146667c93280582f5105914c333144
8eee8a560fc1b68e046e9cb5de9a4c64b146667c93280582f5105914c333144
8eee8a560fc1b68e046e9cb5de9a4c64b146667c93280582f5105914c333144
8eee8a560fc1b68e046e9cb5de9a4c64b146667c93280... [TRUNCATED])delimiter";

http::Record GetExpectedHTTPRecord() {
http::Record expected_record;
expected_record.req.req_method = "GET";
expected_record.req.req_path = "/";
expected_record.req.body = "";

expected_record.resp.resp_status = 200;
expected_record.resp.resp_message = "OK";
return expected_record;
}

using NodeJSServerImplementations = Types<AcornNode14ContainerWrapper>;

TYPED_TEST_SUITE(NodeJSTraceTest, NodeJSServerImplementations);

TYPED_TEST(NodeJSTraceTest, simple_curl_client) {
FLAGS_stirling_conn_trace_pid = this->server_.process_pid();
this->StartTransferDataThread();

// Run the client in the network of the server, so they can connect to each other.
::px::stirling::testing::CurlContainer client;
ASSERT_OK(client.Run(std::chrono::seconds{60},
{absl::Substitute("--network=container:$0", this->server_.container_name())},
{"-s", "-S", "localhost:8080"}));
client.Wait();
this->StopTransferDataThread();

TraceRecords records = this->GetTraceRecords(this->server_.PID());

http::Record expected_record = GetExpectedHTTPRecord();

if (LazyParsingEnabled()) {
// We lazily parse the incomplete chunk with a partial body.
// only the consistent part of the response body is checked
EXPECT_THAT(records.http_records,
UnorderedElementsAre(EqHTTPRecordWithBodyRegex(expected_record, ".*200 4096.*")));

EXPECT_THAT(records.remote_address, UnorderedElementsAre(StrEq("127.0.0.1")));
} else {
// No records should be captured because we exceeded the loop limit (iovec has length 257)
// and lazy parsing was disabled.
EXPECT_THAT(records.http_records, UnorderedElementsAre());
}

auto& registry = GetMetricsRegistry(); // retrieves global var keeping metrics
auto metrics = registry.Collect(); // samples all metrics
auto metrics_text = prometheus::TextSerializer().Serialize(metrics); // serializes to text
LOG(WARNING) << absl::Substitute("with metric text: $0", metrics_text);
}

} // namespace stirling
} // namespace px
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 230,16 @@ pl_cc_test_library(
deps = ["//src/common/testing/test_utils:cc_library"],
)

pl_cc_test_library(
name = "acorn_node14_container",
srcs = [],
hdrs = ["acorn_node14_container.h"],
data = [
"//src/stirling/source_connectors/socket_tracer/testing/containers:nodejs_acorn_image.tar",
],
deps = ["//src/common/testing/test_utils:cc_library"],
)

pl_cc_test_library(
name = "node_client_container",
srcs = [],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 1,48 @@
/*
* Copyright 2018- The Pixie 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.
*
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <string>

#include "src/common/testing/test_environment.h"
#include "src/common/testing/test_utils/container_runner.h"

namespace px {
namespace stirling {
namespace testing {

class AcornNode14Container : public ContainerRunner {
public:
AcornNode14Container()
: ContainerRunner(::px::testing::BazelRunfilePath(kBazelImageTar), kContainerNamePrefix,
kReadyMessage) {}

private:
static constexpr std::string_view kBazelImageTar =
"src/stirling/source_connectors/socket_tracer/testing/containers/"
"nodejs_acorn_image.tar";
static constexpr std::string_view kContainerNamePrefix = "acorn_node_server";
static constexpr std::string_view kReadyMessage = "listening on 8080";
};

// bazel run image to test for same behavior

} // namespace testing
} // namespace stirling
} // namespace px
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 131,18 @@ container_image(
]
]

container_image(
name = "nodejs_acorn_image",
base = "@node_14_18_1_alpine_linux_amd64_image//image",
cmd = [
"node",
"/etc/node/acorn_server.js",
],
layers = [
"//src/stirling/source_connectors/socket_tracer/testing/containers/acornjs:acorn_client_server_layer",
],
)

container_image(
name = "amqp_image",
base = "@rabbitmq_3_management//image",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 1,36 @@
# Copyright 2018- The Pixie 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.
#
# SPDX-License-Identifier: Apache-2.0

load("@io_bazel_rules_docker//container:container.bzl", "container_layer")
load("@rules_pkg//pkg:tar.bzl", "pkg_tar")

package(default_visibility = ["//src/stirling:__subpackages__"])

pkg_tar(
name = "acorn_client_server",
srcs = [
"acorn_client.js",
"acorn_server.js",
],
mode = "0755",
strip_prefix = "/src/stirling/source_connectors/socket_tracer/testing/containers/acornjs",
)

container_layer(
name = "acorn_client_server_layer",
directory = "/etc/node",
tars = [":acorn_client_server"],
)
Original file line number Diff line number Diff line change
@@ -0,0 1,43 @@
/*
* Copyright 2018- The Pixie 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.
*
* SPDX-License-Identifier: Apache-2.0
*/

const https = require('https');

const options = {
hostname: 'localhost',
port: 443,
path: '/index.html',
method: 'GET'
};

// Allows self-signed certs.
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;

const req = https.request(options, (res) => {
console.log('statusCode:', res.statusCode);
console.log('headers:', res.headers);

res.on('data', (d) => {
process.stdout.write(d);
});
});

req.on('error', (e) => {
console.error(e);
});
req.end();
Loading
Loading