diff --git a/.gitignore b/.gitignore index 463ce5df94..85b3571c9d 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ *.pcapng.zst *.pcapng.zstd *.cap +*.snoop #Bin and Obj directories **/Bin diff --git a/Examples/CMakeLists.txt b/Examples/CMakeLists.txt index ce32600a5c..00e371ea71 100644 --- a/Examples/CMakeLists.txt +++ b/Examples/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.12) +cmake_minimum_required(VERSION 3.14) project(PcapPlusPlusExamples) diff --git a/Examples/PcapPlusPlus-benchmark/CMakeLists.txt b/Examples/PcapPlusPlus-benchmark/CMakeLists.txt index 584d6353dd..4d9bfe865f 100644 --- a/Examples/PcapPlusPlus-benchmark/CMakeLists.txt +++ b/Examples/PcapPlusPlus-benchmark/CMakeLists.txt @@ -1,12 +1,50 @@ -add_executable(benchmark benchmark.cpp) +add_executable(BenchmarkExample benchmark.cpp) -target_link_libraries(benchmark PUBLIC PcapPlusPlus::Pcap++) +target_link_libraries(BenchmarkExample PUBLIC PcapPlusPlus::Pcap++) + +set_target_properties(BenchmarkExample PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${PCAPPP_BINARY_EXAMPLES_DIR}") + +if("cxx_std_14" IN_LIST CMAKE_CXX_COMPILE_FEATURES) + if( + (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL CMAKE_SYSTEM_PROCESSOR) + AND (NOT CMAKE_OSX_ARCHITECTURES OR (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL CMAKE_OSX_ARCHITECTURES)) + ) + include(FetchContent) + + # Fetch Google Benchmark + fetchcontent_declare( + benchmark + GIT_REPOSITORY https://github.com/google/benchmark.git + GIT_TAG v1.9.0) + + # Disable testing and installation for Google Benchmark + set(BENCHMARK_ENABLE_TESTING OFF) + set(BENCHMARK_ENABLE_INSTALL OFF) + fetchcontent_makeavailable(benchmark) + + add_executable(BenchmarkExampleGoogle benchmark-google.cpp) + + target_link_libraries(BenchmarkExampleGoogle PUBLIC PcapPlusPlus::Pcap++ benchmark::benchmark) + + set_target_properties(BenchmarkExampleGoogle PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${PCAPPP_BINARY_EXAMPLES_DIR}") + else() + message(WARNING "Google Benchmark backend is not supported for cross-compilation") + endif() +else() + message(WARNING "Google Benchmark backend requires C++14 support") +endif() -set_target_properties(benchmark PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${PCAPPP_BINARY_EXAMPLES_DIR}") if(PCAPPP_INSTALL) install( - TARGETS benchmark + TARGETS BenchmarkExample EXPORT PcapPlusPlusTargets RUNTIME DESTINATION ${PCAPPP_INSTALL_BINDIR}) + + if(TARGET BenchmarkExampleGoogle) + install( + TARGETS BenchmarkExampleGoogle + EXPORT PcapPlusPlusTargets + RUNTIME DESTINATION ${PCAPPP_INSTALL_BINDIR}) + endif() endif() diff --git a/Examples/PcapPlusPlus-benchmark/README.md b/Examples/PcapPlusPlus-benchmark/README.md index 71a4d86429..dc481dcdcf 100644 --- a/Examples/PcapPlusPlus-benchmark/README.md +++ b/Examples/PcapPlusPlus-benchmark/README.md @@ -1,8 +1,19 @@ PcapPlusPlus Benchmark ====================== -This is a benchmark application used for measuring PcapPlusPlus performance. It is based on Matias Fontanini's packet-capture-benchmarks project (https://github.com/mfontanini/packet-capture-benchmarks). +This folder contains benchmark applications for measuring the performance of PcapPlusPlus. Currently, there are two benchmark applications. -See this page for more details: https://pcapplusplus.github.io/docs/benchmark +## Compare with other libraries -This application currently compiles on Linux only (where benchmark was running on) +A benchmark application used for measuring PcapPlusPlus performance can be found in `benchmark.cpp`. It is based on Matias Fontanini's packet-capture-benchmarks project (https://github.com/mfontanini/packet-capture-benchmarks) and allows us to compare PcapPlusPlus with other packet libraries. See this page for more details and result comparisons: https://pcapplusplus.github.io/docs/benchmark + +## Directly benchmark PcapPlusPlus + +Another application integrates with the Google Benchmark library and can be found in `benchmark-google.cpp`. This application currently consists of four different benchmarks, and each benchmark can be influenced by various factors. These benchmarks aim to utilize different influence factors to provide accurate results for different scenarios. You can check the table below for more information. For performance-critical applications using PcapPlusPlus, it is recommended to run benchmarks in your specific environment for more accurate results. Using larger pcap files and those with diverse protocols and sessions can provide better insights into PcapPlusPlus performance in your setup. + +| Benchmark | Operation | Influencing factors | +|:-----------------:|:-------------:|:--------------------:| +| BM_PcapFileRead | Read | CPU + Disk (Read) | +| BM_PcapFileWrite | Write | CPU + Disk (Write) | +| BM_PacketParsing | Read + Parse | CPU + Disk (Read) | +| BM_PacketCrafting | Craft | CPU | diff --git a/Examples/PcapPlusPlus-benchmark/benchmark-google.cpp b/Examples/PcapPlusPlus-benchmark/benchmark-google.cpp new file mode 100644 index 0000000000..e12ccc30e6 --- /dev/null +++ b/Examples/PcapPlusPlus-benchmark/benchmark-google.cpp @@ -0,0 +1,236 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +static std::string pcapFileName = ""; + +static void BM_PcapFileRead(benchmark::State& state) +{ + // Open the pcap file for reading + pcpp::PcapFileReaderDevice reader(pcapFileName); + if (!reader.open()) + { + state.SkipWithError("Cannot open pcap file for reading"); + return; + } + + size_t totalBytes = 0; + size_t totalPackets = 0; + pcpp::RawPacket rawPacket; + for (auto _ : state) + { + if (!reader.getNextPacket(rawPacket)) + { + // If the rawPacket is empty there should be an error + if (totalBytes == 0) + { + state.SkipWithError("Cannot read packet"); + return; + } + + // Rewind the file if it reached the end + state.PauseTiming(); + reader.close(); + reader.open(); + state.ResumeTiming(); + continue; + } + + ++totalPackets; + totalBytes += rawPacket.getRawDataLen(); + } + + state.SetBytesProcessed(totalBytes); + state.SetItemsProcessed(totalPackets); +} +BENCHMARK(BM_PcapFileRead); + +static void BM_PcapFileWrite(benchmark::State& state) +{ + // Open the pcap file for writing + pcpp::PcapFileWriterDevice writer("benchmark-output.pcap"); + if (!writer.open()) + { + state.SkipWithError("Cannot open pcap file for writing"); + return; + } + + pcpp::Packet packet; + pcpp::EthLayer ethLayer(pcpp::MacAddress("00:00:00:00:00:00"), pcpp::MacAddress("00:00:00:00:00:00")); + pcpp::IPv4Layer ip4Layer(pcpp::IPv4Address("192.168.0.1"), pcpp::IPv4Address("192.168.0.2")); + pcpp::TcpLayer tcpLayer(12345, 80); + + packet.addLayer(ðLayer); + packet.addLayer(&ip4Layer); + packet.addLayer(&tcpLayer); + packet.computeCalculateFields(); + + size_t totalBytes = 0; + size_t totalPackets = 0; + for (auto _ : state) + { + // Write packet to file + writer.writePacket(*(packet.getRawPacket())); + + // Count total bytes and packets + ++totalPackets; + totalBytes += packet.getRawPacket()->getRawDataLen(); + } + + // Set statistics to the benchmark state + state.SetBytesProcessed(totalBytes); + state.SetItemsProcessed(totalPackets); +} +BENCHMARK(BM_PcapFileWrite); + +static void BM_PacketParsing(benchmark::State& state) +{ + // Open the pcap file for reading + size_t totalBytes = 0; + size_t totalPackets = 0; + pcpp::PcapFileReaderDevice reader(pcapFileName); + if (!reader.open()) + { + state.SkipWithError("Cannot open pcap file for reading"); + return; + } + + pcpp::RawPacket rawPacket; + for (auto _ : state) + { + if (!reader.getNextPacket(rawPacket)) + { + // If the rawPacket is empty there should be an error + if (totalBytes == 0) + { + state.SkipWithError("Cannot read packet"); + return; + } + + // Rewind the file if it reached the end + state.PauseTiming(); + reader.close(); + reader.open(); + state.ResumeTiming(); + continue; + } + + // Parse packet + pcpp::Packet parsedPacket(&rawPacket); + + // Use parsedPacket to prevent compiler optimizations + assert(parsedPacket.getFirstLayer()); + + // Count total bytes and packets + ++totalPackets; + totalBytes += rawPacket.getRawDataLen(); + } + + // Set statistics to the benchmark state + state.SetBytesProcessed(totalBytes); + state.SetItemsProcessed(totalPackets); +} +BENCHMARK(BM_PacketParsing); + +static void BM_PacketCrafting(benchmark::State& state) +{ + size_t totalBytes = 0; + size_t totalPackets = 0; + + for (auto _ : state) + { + uint8_t randNum = static_cast(rand() % 256); + + pcpp::Packet packet; + + // Generate random MAC addresses + pcpp::MacAddress srcMac(randNum, randNum, randNum, randNum, randNum, randNum); + pcpp::MacAddress dstMac(randNum, randNum, randNum, randNum, randNum, randNum); + packet.addLayer(new pcpp::EthLayer(srcMac, dstMac), true); + + // Randomly choose between IPv4 and IPv6 + if (randNum % 2) + { + packet.addLayer(new pcpp::IPv4Layer(randNum, randNum), true); + } + else + { + std::array srcIP = { randNum, randNum, randNum, randNum, randNum, randNum, randNum, randNum, + randNum, randNum, randNum, randNum, randNum, randNum, randNum, randNum }; + std::array dstIP = { randNum, randNum, randNum, randNum, randNum, randNum, randNum, randNum, + randNum, randNum, randNum, randNum, randNum, randNum, randNum, randNum }; + + packet.addLayer(new pcpp::IPv6Layer(srcIP, dstIP), true); + } + + // Randomly choose between TCP and UDP + if (randNum % 2) + { + packet.addLayer(new pcpp::TcpLayer(randNum % 65536, randNum % 65536), true); + } + else + { + packet.addLayer(new pcpp::UdpLayer(randNum % 65536, randNum % 65536), true); + } + + // Calculate all fields to update the packet + packet.computeCalculateFields(); + + // Count total bytes and packets + ++totalPackets; + totalBytes += packet.getRawPacket()->getRawDataLen(); + } + + // Set statistics to the benchmark state + state.SetBytesProcessed(totalBytes); + state.SetItemsProcessed(totalPackets); +} +BENCHMARK(BM_PacketCrafting); + +int main(int argc, char** argv) +{ + // Initialize the benchmark + benchmark::Initialize(&argc, argv); + + // Parse command line arguments to find the pcap file name + for (int idx = 1; idx < argc; ++idx) + { + if (strcmp(argv[idx], "--pcap-file") == 0) + { + if (idx == argc - 1) + { + std::cerr << "Please provide a pcap file name after --pcap-file" << std::endl; + return 1; + } + + pcapFileName = argv[idx + 1]; + break; + } + } + + if (pcapFileName.empty()) + { + std::cerr << "Please provide a pcap file name using --pcap-file" << std::endl; + return 1; + } + + benchmark::AddCustomContext("PcapPlusPlus version", pcpp::getPcapPlusPlusVersionFull()); + benchmark::AddCustomContext("Build info", pcpp::getBuildDateTime()); + benchmark::AddCustomContext("Git info", pcpp::getGitInfo()); + benchmark::AddCustomContext("Pcap file", pcapFileName); + + // Run the benchmarks + benchmark::RunSpecifiedBenchmarks(); + + return 0; +}