mirror of
https://github.com/falcosecurity/falco.git
synced 2026-03-20 03:32:09 +00:00
Compare commits
20 Commits
0.43.0-rc2
...
new/profil
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c6c2f7b02 | ||
|
|
8dfc5da425 | ||
|
|
cc8bc663df | ||
|
|
35cba8a231 | ||
|
|
694f13c9a2 | ||
|
|
e17fb1cb04 | ||
|
|
943d49bdf2 | ||
|
|
f8783b3312 | ||
|
|
96df786ce8 | ||
|
|
54050b2d4a | ||
|
|
dac2ff9379 | ||
|
|
7e048f384c | ||
|
|
43446369ec | ||
|
|
e8afe72063 | ||
|
|
1710812065 | ||
|
|
b21f09aebd | ||
|
|
02853d5310 | ||
|
|
755ba8f9a2 | ||
|
|
d2d7ead732 | ||
|
|
fb948912a3 |
@@ -17,8 +17,8 @@ project(falco)
|
||||
option(USE_BUNDLED_DEPS "Bundle hard to find dependencies into the Falco binary" OFF)
|
||||
option(BUILD_WARNINGS_AS_ERRORS "Enable building with -Wextra -Werror flags" OFF)
|
||||
|
||||
# Elapsed time
|
||||
# set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CMAKE_COMMAND} -E time") # TODO(fntlnz, leodido): add a flag to enable this
|
||||
# Elapsed time set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CMAKE_COMMAND} -E time") # TODO(fntlnz, leodido): add
|
||||
# a flag to enable this
|
||||
|
||||
# Make flag for parallel processing
|
||||
include(ProcessorCount)
|
||||
@@ -99,8 +99,7 @@ ExternalProject_Add(
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND "")
|
||||
|
||||
# curses
|
||||
# We pull this in because libsinsp won't build without it
|
||||
# curses We pull this in because libsinsp won't build without it
|
||||
set(CURSES_NEED_NCURSES TRUE)
|
||||
find_package(Curses REQUIRED)
|
||||
message(STATUS "Found ncurses: include: ${CURSES_INCLUDE_DIR}, lib: ${CURSES_LIBRARIES}")
|
||||
@@ -215,6 +214,12 @@ ExternalProject_Add(
|
||||
BUILD_COMMAND ${CMD_MAKE} COPT="-DNO_FILES" WITH_CPP=1
|
||||
INSTALL_COMMAND ${CMD_MAKE} COPT="-DNO_FILES" install-lib install-headers PREFIX=${CIVETWEB_SRC}/install "WITH_CPP=1")
|
||||
|
||||
# Hedley
|
||||
include(DownloadHedley)
|
||||
|
||||
# FlatBuffers
|
||||
include(FlatBuffers)
|
||||
|
||||
# gRPC
|
||||
include(gRPC)
|
||||
|
||||
@@ -236,8 +241,8 @@ add_subdirectory(rules)
|
||||
# Dockerfiles
|
||||
add_subdirectory(docker)
|
||||
|
||||
# Clang format
|
||||
# add_custom_target(format COMMAND clang-format --style=file -i $<TARGET_PROPERTY:falco,SOURCES> COMMENT "Formatting ..." VERBATIM)
|
||||
# Clang format add_custom_target(format COMMAND clang-format --style=file -i $<TARGET_PROPERTY:falco,SOURCES> COMMENT
|
||||
# "Formatting ..." VERBATIM)
|
||||
|
||||
# Shared build variables
|
||||
set(FALCO_SINSP_LIBRARY sinsp)
|
||||
@@ -246,6 +251,7 @@ set(FALCO_ABSOLUTE_SHARE_DIR "${CMAKE_INSTALL_PREFIX}/${FALCO_SHARE_DIR}")
|
||||
set(FALCO_BIN_DIR bin)
|
||||
|
||||
add_subdirectory(scripts)
|
||||
add_subdirectory(userspace/profiler)
|
||||
add_subdirectory(userspace/engine)
|
||||
add_subdirectory(userspace/falco)
|
||||
add_subdirectory(tests)
|
||||
|
||||
@@ -12,16 +12,17 @@
|
||||
#
|
||||
include(ExternalProject)
|
||||
|
||||
set(CATCH2_INCLUDE ${CMAKE_BINARY_DIR}/catch2-prefix/include)
|
||||
set(CATCH2_PREFIX ${CMAKE_BINARY_DIR}/catch2-prefix)
|
||||
set(CATCH2_INCLUDE ${CATCH2_PREFIX}/include)
|
||||
|
||||
set(CATCH_EXTERNAL_URL URL https://github.com/catchorg/catch2/archive/v2.12.1.tar.gz URL_HASH
|
||||
SHA256=e5635c082282ea518a8dd7ee89796c8026af8ea9068cd7402fb1615deacd91c3)
|
||||
|
||||
ExternalProject_Add(
|
||||
catch2
|
||||
PREFIX ${CMAKE_BINARY_DIR}/catch2-prefix
|
||||
PREFIX ${CATCH2_PREFIX}
|
||||
${CATCH_EXTERNAL_URL}
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/catch2-prefix/src/catch2/single_include/catch2/catch.hpp
|
||||
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy ${CATCH2_PREFIX}/src/catch2/single_include/catch2/catch.hpp
|
||||
${CATCH2_INCLUDE}/catch.hpp)
|
||||
|
||||
26
cmake/modules/DownloadHedley.cmake
Normal file
26
cmake/modules/DownloadHedley.cmake
Normal file
@@ -0,0 +1,26 @@
|
||||
#
|
||||
# Copyright (C) 2019 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(ExternalProject)
|
||||
|
||||
set(HEDLEY_PREFIX ${CMAKE_BINARY_DIR}/hedley-prefix)
|
||||
set(HEDLEY_INCLUDE ${HEDLEY_PREFIX}/include)
|
||||
message(STATUS "Found hedley: include: ${HEDLEY_INCLUDE}")
|
||||
|
||||
ExternalProject_Add(
|
||||
hedley
|
||||
PREFIX ${HEDLEY_PREFIX}
|
||||
GIT_REPOSITORY "https://github.com/nemequ/hedley.git"
|
||||
GIT_TAG "v13"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy ${HEDLEY_PREFIX}/src/hedley/hedley.h ${HEDLEY_INCLUDE}/hedley.h)
|
||||
82
cmake/modules/FlatBuffers.cmake
Normal file
82
cmake/modules/FlatBuffers.cmake
Normal file
@@ -0,0 +1,82 @@
|
||||
#
|
||||
# Copyright (C) 2020 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.
|
||||
#
|
||||
|
||||
if(NOT USE_BUNDLED_DEPS)
|
||||
find_program(FLATBUFFERS_FLATC_EXECUTABLE NAMES flatc)
|
||||
find_path(FLATBUFFERS_INCLUDE_DIR NAMES flatbuffers/flatbuffers.h)
|
||||
|
||||
if(FLATBUFFERS_FLATC_EXECUTABLE AND FLATBUFFERS_INCLUDE_DIR)
|
||||
message(STATUS "Found flatbuffers: include: ${FLATBUFFERS_INCLUDE_DIR}, flatc: ${FLATBUFFERS_FLATC_EXECUTABLE}")
|
||||
else()
|
||||
message(FATAL_ERROR "Couldn't find system flatbuffers")
|
||||
endif()
|
||||
else()
|
||||
include(ExternalProject)
|
||||
|
||||
set(FLATBUFFERS_PREFIX ${CMAKE_BINARY_DIR}/flatbuffers-prefix)
|
||||
set(FLATBUFFERS_FLATC_EXECUTABLE
|
||||
${FLATBUFFERS_PREFIX}/bin/flatc
|
||||
CACHE INTERNAL "FlatBuffer compiler")
|
||||
set(FLATBUFFERS_INCLUDE_DIR
|
||||
${FLATBUFFERS_PREFIX}/include
|
||||
CACHE INTERNAL "FlatBuffer include directory")
|
||||
|
||||
ExternalProject_Add(
|
||||
flatbuffers
|
||||
PREFIX ${FLATBUFFERS_PREFIX}
|
||||
GIT_REPOSITORY "https://github.com/google/flatbuffers.git"
|
||||
GIT_TAG "v1.12.0"
|
||||
CMAKE_ARGS
|
||||
-DCMAKE_INSTALL_PREFIX=${FLATBUFFERS_PREFIX}
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
-DFLATBUFFERS_CODE_COVERAGE=OFF
|
||||
-DFLATBUFFERS_BUILD_TESTS=OFF
|
||||
-DFLATBUFFERS_INSTALL=ON
|
||||
-DFLATBUFFERS_BUILD_FLATLIB=OFF
|
||||
-DFLATBUFFERS_BUILD_FLATC=ON
|
||||
-DFLATBUFFERS_BUILD_FLATHASH=OFF
|
||||
-DFLATBUFFERS_BUILD_GRPCTEST=OFF
|
||||
-DFLATBUFFERS_BUILD_SHAREDLIB=OFF
|
||||
BUILD_BYPRODUCTS ${FLATBUFFERS_FLATC_EXECUTABLE})
|
||||
endif()
|
||||
|
||||
# From FindFlatBuffer.cmake
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(FlatBuffers DEFAULT_MSG FLATBUFFERS_FLATC_EXECUTABLE FLATBUFFERS_INCLUDE_DIR)
|
||||
|
||||
if(FLATBUFFERS_FOUND)
|
||||
function(FLATBUFFERS_GENERATE_C_HEADERS Name)
|
||||
set(FLATC_OUTPUTS)
|
||||
foreach(FILE ${ARGN})
|
||||
get_filename_component(FLATC_OUTPUT ${FILE} NAME_WE)
|
||||
set(FLATC_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FLATC_OUTPUT}_generated.h")
|
||||
|
||||
list(APPEND FLATC_OUTPUTS ${FLATC_OUTPUT})
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${FLATC_OUTPUT}
|
||||
COMMAND ${FLATBUFFERS_FLATC_EXECUTABLE} ARGS -c -o "${CMAKE_CURRENT_BINARY_DIR}/" ${FILE}
|
||||
DEPENDS ${FILE}
|
||||
COMMENT "Building C++ header for ${FILE}"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
endforeach()
|
||||
set(${Name}_OUTPUTS
|
||||
${FLATC_OUTPUTS}
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
set(FLATBUFFERS_INCLUDE_DIRS ${FLATBUFFERS_INCLUDE_DIR})
|
||||
include_directories(${CMAKE_BINARY_DIR})
|
||||
else()
|
||||
set(FLATBUFFERS_INCLUDE_DIR)
|
||||
endif()
|
||||
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (C) 2019 The Falco Authors.
|
||||
# Copyright (C) 2020 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
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
# License for the specific language governing permissions and limitations under
|
||||
# the License.
|
||||
#
|
||||
set(FALCO_TESTS_SOURCES test_base.cpp engine/test_token_bucket.cpp engine/test_rulesets.cpp falco/test_webserver.cpp)
|
||||
set(FALCO_TESTS_SOURCES test_base.cpp engine/test_token_bucket.cpp falco/test_webserver.cpp engine/test_rulesets.cpp profiler/test_profiler.cpp)
|
||||
|
||||
set(FALCO_TESTED_LIBRARIES falco_engine)
|
||||
set(FALCO_TESTED_LIBRARIES falco_engine falco_profiler)
|
||||
|
||||
SET(FALCO_TESTS_ARGUMENTS "" CACHE STRING "Test arguments to pass to the Falco test suite")
|
||||
|
||||
@@ -43,12 +43,12 @@ if(FALCO_BUILD_TESTS)
|
||||
"${YAMLCPP_INCLUDE_DIR}"
|
||||
"${CIVETWEB_INCLUDE_DIR}"
|
||||
"${PROJECT_SOURCE_DIR}/userspace/falco")
|
||||
add_dependencies(falco_test catch2)
|
||||
add_dependencies(falco_test catch2 hedley)
|
||||
|
||||
include(CMakeParseArguments)
|
||||
include(CTest)
|
||||
include(Catch)
|
||||
catch_discover_tests(falco_test)
|
||||
separate_arguments(FALCO_TESTS_ARGUMENTS)
|
||||
add_custom_target(tests COMMAND ${CMAKE_CTEST_COMMAND} ${FALCO_TESTS_ARGUMENTS} DEPENDS falco_test)
|
||||
add_custom_target(tests COMMAND ${CMAKE_CTEST_COMMAND} ${FALCO_TESTS_ARGUMENTS} DEPENDS falco_test flatbuffer_profiler)
|
||||
endif()
|
||||
|
||||
353
tests/profiler/test_profiler.cpp
Normal file
353
tests/profiler/test_profiler.cpp
Normal file
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
Copyright (C) 2020 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 "profiler.h"
|
||||
#include <catch.hpp>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
#include <hedley.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include <flatbuffers/flatbuffers.h>
|
||||
#include "profile_generated.h"
|
||||
|
||||
// void profile_recurse(int times)
|
||||
// {
|
||||
// PROFILEME();
|
||||
// for(int i = 1; i < times; i++)
|
||||
// {
|
||||
// profile_recurse(1);
|
||||
// }
|
||||
// }
|
||||
|
||||
void profiler_sleep_1sec()
|
||||
{
|
||||
PROFILEME();
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
void profiler_sleep_2sec()
|
||||
{
|
||||
PROFILEME();
|
||||
profiler_sleep_1sec();
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
TEST_CASE("profiler stores labels and chunks", "[profiler]")
|
||||
{
|
||||
SECTION("profiler needs to be allocated before usage")
|
||||
{
|
||||
profiler::alloc_chunk();
|
||||
REQUIRE(profiler::chunks.size() == 1);
|
||||
REQUIRE(profiler::labels.empty());
|
||||
}
|
||||
|
||||
SECTION("calling a profiled function results in labels and chunks increase")
|
||||
{
|
||||
profiler_sleep_1sec();
|
||||
REQUIRE(profiler::labels.size() == 1);
|
||||
REQUIRE(profiler::chunks.size() == 1);
|
||||
}
|
||||
|
||||
SECTION("calling just another profiled function that calls the one previously called should only increase labels by one")
|
||||
{
|
||||
profiler_sleep_2sec();
|
||||
REQUIRE(profiler::labels.size() == 2);
|
||||
REQUIRE(profiler::chunks.size() == 1);
|
||||
}
|
||||
|
||||
SECTION("clean")
|
||||
{
|
||||
profiler::labels.clear();
|
||||
profiler::chunks.clear();
|
||||
REQUIRE(profiler::labels.empty());
|
||||
REQUIRE(profiler::chunks.empty());
|
||||
}
|
||||
}
|
||||
|
||||
void bb()
|
||||
{
|
||||
PROFILEME();
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||
}
|
||||
|
||||
void aa()
|
||||
{
|
||||
PROFILEME();
|
||||
bb();
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||
}
|
||||
|
||||
void aa_threaded()
|
||||
{
|
||||
profiler::alloc_chunk();
|
||||
PROFILEME();
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||
}
|
||||
|
||||
void cc()
|
||||
{
|
||||
PROFILEME();
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||
}
|
||||
|
||||
void profiler_fake_root_func()
|
||||
{
|
||||
PROFILEME();
|
||||
aa();
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||
bb();
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||
cc();
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||
aa();
|
||||
}
|
||||
|
||||
TEST_CASE("profiler computes parents (single chunk)", "[profiler]")
|
||||
{
|
||||
profiler::alloc_chunk();
|
||||
profiler_fake_root_func();
|
||||
|
||||
REQUIRE(profiler::labels.size() == 4);
|
||||
REQUIRE(profiler::chunks.size() == 1);
|
||||
REQUIRE(profiler::chunks[0].begin[5] == 0); // profiler_fake_root_func is root (parent = 0)
|
||||
|
||||
auto predicate = [](int x) {
|
||||
return [=](const profiler::label &l) {
|
||||
return l.label == profiler::chunks[0].begin[x];
|
||||
};
|
||||
};
|
||||
|
||||
// Ctor ordering:
|
||||
// profile_fake_root
|
||||
// AA1
|
||||
// BB1
|
||||
// BB2
|
||||
// CC1
|
||||
// AA2
|
||||
// BB3
|
||||
|
||||
// Dtor ordering:
|
||||
// BB1
|
||||
// AA1
|
||||
// BB2
|
||||
// CC1
|
||||
// BB3
|
||||
// AA2
|
||||
// profile_fake_root
|
||||
|
||||
auto root = std::find_if(std::begin(profiler::labels), std::end(profiler::labels), predicate(0));
|
||||
REQUIRE(root->func == "profiler_fake_root_func"); // function name is "profiler_fake_root_func"
|
||||
REQUIRE(profiler::chunks[0].begin[5 + CHUNK_ELEMENTS] == root->label); // aa (call aa1) has parent "profiler_fake_root_func"
|
||||
|
||||
{
|
||||
auto it = std::find_if(std::begin(profiler::labels), std::end(profiler::labels), predicate(CHUNK_ELEMENTS));
|
||||
REQUIRE(it->func == "aa"); // function name is "aa" (call aa1)
|
||||
REQUIRE(profiler::chunks[0].begin[5 + (2 * CHUNK_ELEMENTS)] == it->label); // bb (call bb1) has parent "aa" (call aa1)
|
||||
}
|
||||
|
||||
{
|
||||
auto it = std::find_if(std::begin(profiler::labels), std::end(profiler::labels), predicate(2 * CHUNK_ELEMENTS));
|
||||
REQUIRE(it->func == "bb"); // function name is "bb" (call bb1)
|
||||
REQUIRE(profiler::chunks[0].begin[5 + (3 * CHUNK_ELEMENTS)] == root->label); // bb (call bb2) has parent "profiler_fake_root_func"
|
||||
}
|
||||
|
||||
{
|
||||
auto it = std::find_if(std::begin(profiler::labels), std::end(profiler::labels), predicate(3 * CHUNK_ELEMENTS));
|
||||
REQUIRE(it->func == "bb"); // function name is "bb" (call bb2)
|
||||
REQUIRE(profiler::chunks[0].begin[5 + (4 * CHUNK_ELEMENTS)] == root->label); // cc (call cc1) has parent "profiler_fake_root_func"
|
||||
}
|
||||
|
||||
{
|
||||
auto it = std::find_if(std::begin(profiler::labels), std::end(profiler::labels), predicate(4 * CHUNK_ELEMENTS));
|
||||
REQUIRE(it->func == "cc"); // function name is "cc" (call cc1)
|
||||
REQUIRE(profiler::chunks[0].begin[5 + (5 * CHUNK_ELEMENTS)] == root->label); // aa (call aa2) has parent "profiler_fake_root_func"
|
||||
}
|
||||
|
||||
{
|
||||
auto it = std::find_if(std::begin(profiler::labels), std::end(profiler::labels), predicate(5 * CHUNK_ELEMENTS));
|
||||
REQUIRE(it->func == "aa"); // function name is "aa" (call aa2)
|
||||
REQUIRE(profiler::chunks[0].begin[5 + (6 * CHUNK_ELEMENTS)] == it->label); // bb (call bb3) has parent "aa" (call aa2)
|
||||
}
|
||||
|
||||
{
|
||||
auto it = std::find_if(std::begin(profiler::labels), std::end(profiler::labels), predicate(6 * CHUNK_ELEMENTS));
|
||||
REQUIRE(it->func == "bb"); // function name is "bb" (call bb3)
|
||||
}
|
||||
|
||||
for(std::vector<int>::size_type j = 0; j != profiler::chunks.size(); j++)
|
||||
{
|
||||
auto *c = profiler::chunks[j].begin;
|
||||
for(int i = 0; i < CHUNK_SIZE; i += CHUNK_ELEMENTS)
|
||||
{
|
||||
if(c[i] == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
auto parent = c[i + 5];
|
||||
auto e = ((unsigned long long)c[i + 4]) | (((unsigned long long)c[i + 3]) << 32);
|
||||
auto s = ((unsigned long long)c[i + 2]) | (((unsigned long long)c[i + 1]) << 32);
|
||||
char b[1024];
|
||||
if(i == 0)
|
||||
{
|
||||
sprintf(b, "#;idx;parent ;function ;clocks");
|
||||
std::cout << std::string(b) << std::endl;
|
||||
}
|
||||
sprintf(b, "%ld;%03d;%010u;%u;%lld", j, i, parent, c[i], e - s);
|
||||
std::cout << std::string(b) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
profiler::labels.clear();
|
||||
profiler::chunks.clear();
|
||||
REQUIRE(profiler::labels.empty());
|
||||
REQUIRE(profiler::chunks.empty());
|
||||
}
|
||||
// TEST_CASE("profile computes recursive parents (more chunks)", "[profiler]")
|
||||
// {
|
||||
// alloc_chunk();
|
||||
// REQUIRE(chunks.size() == 1);
|
||||
|
||||
// size_t expected_chunks = 2;
|
||||
// int how_many_times = (CHUNK_SIZE / CHUNK_ELEMENTS) * HEDLEY_STATIC_CAST(int, expected_chunks);
|
||||
|
||||
// profile_recurse(how_many_times);
|
||||
|
||||
// REQUIRE(labels.size() == 1);
|
||||
// REQUIRE(chunks.size() == expected_chunks + 1);
|
||||
|
||||
// for(std::vector<int>::size_type j = 0; j != chunks.size(); j++)
|
||||
// {
|
||||
// auto *c = chunks[j].begin;
|
||||
// for(int i = 0; i < CHUNK_SIZE; i++)
|
||||
// {
|
||||
// char b[1024];
|
||||
// sprintf(b, "%ld - %03d: %u", j, i, c[i]);
|
||||
// std::cout << std::string(b) << std::endl;
|
||||
// }
|
||||
// }
|
||||
|
||||
// labels.clear();
|
||||
// chunks.clear();
|
||||
// REQUIRE(labels.empty());
|
||||
// REQUIRE(chunks.empty());
|
||||
// }
|
||||
|
||||
TEST_CASE("profiler flatbuffer serialization deserialization", "[profiler]")
|
||||
{
|
||||
flatbuffers::FlatBufferBuilder builder;
|
||||
|
||||
auto node_one = profiler::CreateNodeDirect(
|
||||
builder,
|
||||
"do_init",
|
||||
"falco_engine.cpp",
|
||||
345,
|
||||
500);
|
||||
|
||||
auto node_two_one = profiler::CreateNodeDirect(
|
||||
builder,
|
||||
"do_match2",
|
||||
"ruleset.cpp",
|
||||
690,
|
||||
100);
|
||||
|
||||
std::vector<flatbuffers::Offset<profiler::Node>> third_level;
|
||||
third_level.push_back(node_two_one);
|
||||
|
||||
auto node_two = profiler::CreateNodeDirect(
|
||||
builder,
|
||||
"do_match",
|
||||
"ruleset.cpp",
|
||||
678,
|
||||
250,
|
||||
&third_level);
|
||||
|
||||
std::vector<flatbuffers::Offset<profiler::Node>> second_level;
|
||||
second_level.push_back(node_one);
|
||||
second_level.push_back(node_two);
|
||||
|
||||
auto root = profiler::CreateNodeDirect(
|
||||
builder,
|
||||
"main",
|
||||
"falco.cpp",
|
||||
123,
|
||||
750,
|
||||
&second_level);
|
||||
|
||||
profiler::FinishNodeBuffer(builder, root);
|
||||
|
||||
SECTION("binary output has identifier")
|
||||
{
|
||||
REQUIRE(profiler::NodeBufferHasIdentifier(builder.GetBufferPointer()) == true);
|
||||
}
|
||||
|
||||
SECTION("deserialization")
|
||||
{
|
||||
auto node = profiler::GetNode(builder.GetBufferPointer());
|
||||
|
||||
REQUIRE(node->cycles() == 750);
|
||||
REQUIRE(node->line() == 123);
|
||||
REQUIRE(node->func()->str() == "main");
|
||||
REQUIRE(node->file()->str() == "falco.cpp");
|
||||
REQUIRE(node->childs() != nullptr);
|
||||
REQUIRE(node->childs()->size() == 2);
|
||||
REQUIRE(node->childs()->Get(0) == flatbuffers::GetTemporaryPointer(builder, node_one));
|
||||
REQUIRE(node->childs()->Get(1) == flatbuffers::GetTemporaryPointer(builder, node_two));
|
||||
|
||||
auto expect_one = node->childs()->Get(0);
|
||||
REQUIRE(expect_one->cycles() == 500);
|
||||
REQUIRE(expect_one->line() == 345);
|
||||
REQUIRE(expect_one->func()->str() == "do_init");
|
||||
REQUIRE(expect_one->file()->str() == "falco_engine.cpp");
|
||||
REQUIRE(expect_one->childs() == nullptr);
|
||||
|
||||
auto expect_two = node->childs()->Get(1);
|
||||
REQUIRE(expect_two->cycles() == 250);
|
||||
REQUIRE(expect_two->line() == 678);
|
||||
REQUIRE(expect_two->func()->str() == "do_match");
|
||||
REQUIRE(expect_two->file()->str() == "ruleset.cpp");
|
||||
REQUIRE(expect_two->childs() != nullptr);
|
||||
REQUIRE(expect_two->childs()->size() == 1);
|
||||
REQUIRE(expect_two->childs()->Get(0) == flatbuffers::GetTemporaryPointer(builder, node_two_one));
|
||||
|
||||
auto expect_two_one = expect_two->childs()->Get(0);
|
||||
REQUIRE(expect_two_one->cycles() == 100);
|
||||
REQUIRE(expect_two_one->line() == 690);
|
||||
REQUIRE(expect_two_one->func()->str() == "do_match2");
|
||||
REQUIRE(expect_two_one->file()->str() == "ruleset.cpp");
|
||||
REQUIRE(expect_two_one->childs() == nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void profile_fake_root_launching_threads()
|
||||
{
|
||||
PROFILEME();
|
||||
std::thread ta(aa_threaded);
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||
bb();
|
||||
|
||||
ta.join();
|
||||
}
|
||||
|
||||
TEST_CASE("profiler multi threading", "[profiler]")
|
||||
{
|
||||
profiler::alloc_chunk();
|
||||
REQUIRE(profiler::chunks.size() == 1);
|
||||
profile_fake_root_launching_threads();
|
||||
}
|
||||
@@ -69,6 +69,8 @@ target_include_directories(
|
||||
falco
|
||||
PUBLIC
|
||||
"${SYSDIG_SOURCE_DIR}/userspace/sysdig"
|
||||
"${HEDLEY_INCLUDE}"
|
||||
"${PROJECT_SOURCE_DIR}/userspace/profiler"
|
||||
"${PROJECT_SOURCE_DIR}/userspace/engine"
|
||||
"${PROJECT_BINARY_DIR}/userspace/falco"
|
||||
"${PROJECT_BINARY_DIR}/driver/src"
|
||||
@@ -84,6 +86,7 @@ target_link_libraries(
|
||||
falco
|
||||
falco_engine
|
||||
sinsp
|
||||
falco_profiler
|
||||
"${GPR_LIB}"
|
||||
"${GRPC_LIB}"
|
||||
"${GRPCPP_LIB}"
|
||||
|
||||
@@ -45,6 +45,7 @@ limitations under the License.
|
||||
#include "statsfilewriter.h"
|
||||
#include "webserver.h"
|
||||
#include "grpc_server.h"
|
||||
#include "profiler.h"
|
||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||
|
||||
typedef function<void(sinsp* inspector)> open_t;
|
||||
@@ -235,6 +236,7 @@ uint64_t do_inspect(falco_engine *engine,
|
||||
bool all_events,
|
||||
int &result)
|
||||
{
|
||||
PROFILEME();
|
||||
uint64_t num_evts = 0;
|
||||
int32_t rc;
|
||||
sinsp_evt* ev;
|
||||
@@ -1291,6 +1293,7 @@ exit:
|
||||
//
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
alloc_chunk();
|
||||
int rc;
|
||||
|
||||
// g_restart will cause the falco loop to exit, but we
|
||||
|
||||
19
userspace/profiler/CMakeLists.txt
Normal file
19
userspace/profiler/CMakeLists.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
#
|
||||
# Copyright (C) 2020 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.
|
||||
|
||||
add_library(falco_profiler INTERFACE)
|
||||
|
||||
target_include_directories(falco_profiler INTERFACE "${HEDLEY_INCLUDE}" "${FLATBUFFER_INCLUDE_DIR}"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}")
|
||||
|
||||
flatbuffers_generate_c_headers(PROFILE "profile.fbs")
|
||||
add_custom_target(flatbuffer_profiler DEPENDS ${PROFILE_OUTPUTS})
|
||||
14
userspace/profiler/profile.fbs
Normal file
14
userspace/profiler/profile.fbs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace profiler;
|
||||
|
||||
file_identifier "FPRO";
|
||||
file_extension "fpro";
|
||||
|
||||
table Node {
|
||||
func:string;
|
||||
file:string;
|
||||
line:int;
|
||||
cycles:uint64;
|
||||
childs:[Node];
|
||||
}
|
||||
|
||||
root_type Node;
|
||||
172
userspace/profiler/profiler.h
Normal file
172
userspace/profiler/profiler.h
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
Copyright (C) 2020 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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <hedley.h>
|
||||
|
||||
namespace profiler
|
||||
{
|
||||
|
||||
#define CHUNK_ELEMENTS 7
|
||||
#define CHUNK_SIZE ((1 << 20) * CHUNK_ELEMENTS) // 20 MiB = 5242880 * sizeof(uint32_t)
|
||||
#define LABEL_MASK 0x80000000 // Largest positive int32 + 1
|
||||
|
||||
struct cursor
|
||||
{
|
||||
uint32_t* current;
|
||||
uint32_t* end;
|
||||
};
|
||||
|
||||
struct chunk
|
||||
{
|
||||
uint32_t* begin;
|
||||
uint32_t* end;
|
||||
std::thread::id thread;
|
||||
};
|
||||
|
||||
struct label
|
||||
{
|
||||
uint32_t label;
|
||||
std::string file;
|
||||
std::string func;
|
||||
int line;
|
||||
};
|
||||
|
||||
thread_local cursor c;
|
||||
std::vector<label> labels;
|
||||
std::vector<chunk> chunks;
|
||||
size_t nchunks;
|
||||
std::mutex mu;
|
||||
|
||||
HEDLEY_NEVER_INLINE void alloc_chunk()
|
||||
{
|
||||
auto d = new uint32_t[CHUNK_SIZE];
|
||||
c.current = d;
|
||||
c.end = d + CHUNK_SIZE;
|
||||
|
||||
mu.lock();
|
||||
chunks.push_back({d, d + CHUNK_SIZE, std::this_thread::get_id()});
|
||||
nchunks += 1;
|
||||
mu.unlock();
|
||||
}
|
||||
|
||||
HEDLEY_NEVER_INLINE uint32_t create_label(char const* file, int line, char const* func)
|
||||
{
|
||||
label l;
|
||||
l.label = labels.size() | LABEL_MASK;
|
||||
l.file = file;
|
||||
l.func = func;
|
||||
l.line = line;
|
||||
mu.lock();
|
||||
labels.push_back(l);
|
||||
mu.unlock();
|
||||
return l.label;
|
||||
}
|
||||
|
||||
struct profile
|
||||
{
|
||||
uint32_t* data;
|
||||
int n;
|
||||
int epochs; // (max) depth at the moment of execution
|
||||
|
||||
explicit profile(uint32_t label)
|
||||
{
|
||||
data = c.current;
|
||||
auto next = data + CHUNK_ELEMENTS;
|
||||
n = nchunks;
|
||||
epochs = (next - chunks[n - 1].begin) / CHUNK_ELEMENTS; // (next - start) / data size
|
||||
|
||||
if(HEDLEY_LIKELY(next != c.end))
|
||||
c.current = next; // adds 28 bytes
|
||||
else
|
||||
alloc_chunk(); // note: changes `c` values (current and end)
|
||||
|
||||
data[0] = label;
|
||||
data[5] = 0; // unknown parent
|
||||
data[6] = 0; // mark current instance as init
|
||||
|
||||
unsigned int lo, hi;
|
||||
__asm__ __volatile__("rdtsc"
|
||||
: "=a"(lo), "=d"(hi));
|
||||
data[1] = hi;
|
||||
data[2] = lo;
|
||||
}
|
||||
|
||||
~profile()
|
||||
{
|
||||
unsigned int lo, hi;
|
||||
__asm__ __volatile__("rdtsc"
|
||||
: "=a"(lo), "=d"(hi));
|
||||
data[3] = hi;
|
||||
data[4] = lo;
|
||||
|
||||
if(HEDLEY_LIKELY(epochs > 1))
|
||||
{
|
||||
for(int i = 0; i < epochs - 1; i++)
|
||||
{
|
||||
// Check whether the i-th destructor before this has been called (>0) or not (0)
|
||||
if(data[-1 - (i * CHUNK_ELEMENTS)] == 0)
|
||||
{
|
||||
// The head of the first destructor which has not been called yet is the parent of the current one
|
||||
data[5] = data[-((i + 1) * CHUNK_ELEMENTS)];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(n > 1)
|
||||
{
|
||||
// TODO: make it span across more chunks (until n - 2 == 0)
|
||||
uint32_t* cdata = chunks[n - 2].end;
|
||||
for(int i = 0; i < (CHUNK_SIZE / CHUNK_ELEMENTS); i++)
|
||||
{
|
||||
if(cdata[-1 - (i * CHUNK_ELEMENTS)] == 0)
|
||||
{
|
||||
data[5] = cdata[-((i + 1) * CHUNK_ELEMENTS)];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(n > 1 && data[5] == 0)
|
||||
{
|
||||
// TODO: same as above
|
||||
// TODO: make it span across more chunks (until n - 2 == 0)
|
||||
uint32_t* cdata = chunks[n - 2].end;
|
||||
for(int i = 0; i < (CHUNK_SIZE / CHUNK_ELEMENTS); i++)
|
||||
{
|
||||
if(cdata[-1 - (i * CHUNK_ELEMENTS)] == 0)
|
||||
{
|
||||
data[5] = cdata[-((i + 1) * CHUNK_ELEMENTS)];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
data[6] = n; // mark current instance as done storing the chunk index (+ 1)
|
||||
}
|
||||
};
|
||||
|
||||
#define PROFILE_VARIABLE_IMPL(arg1, arg2) arg1##arg2
|
||||
#define PROFILE_VARIABLE(arg1, arg2) PROFILE_VARIABLE_IMPL(arg1, arg2)
|
||||
|
||||
#define PROFILEME() \
|
||||
static uint32_t PROFILE_VARIABLE(_label, __LINE__) = profiler::create_label(__FILE__, __LINE__, __FUNCTION__); \
|
||||
profiler::profile PROFILE_VARIABLE(_profile_, __LINE__)(PROFILE_VARIABLE(_label, __LINE__));
|
||||
|
||||
}; // namespace profiler
|
||||
Reference in New Issue
Block a user