mirror of
https://github.com/falcosecurity/falco.git
synced 2026-03-30 16:42:34 +00:00
Compare commits
20 Commits
buffer_dim
...
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(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)
|
option(BUILD_WARNINGS_AS_ERRORS "Enable building with -Wextra -Werror flags" OFF)
|
||||||
|
|
||||||
# Elapsed time
|
# Elapsed time set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CMAKE_COMMAND} -E time") # TODO(fntlnz, leodido): add
|
||||||
# set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CMAKE_COMMAND} -E time") # TODO(fntlnz, leodido): add a flag to enable this
|
# a flag to enable this
|
||||||
|
|
||||||
# Make flag for parallel processing
|
# Make flag for parallel processing
|
||||||
include(ProcessorCount)
|
include(ProcessorCount)
|
||||||
@@ -99,8 +99,7 @@ ExternalProject_Add(
|
|||||||
BUILD_COMMAND ""
|
BUILD_COMMAND ""
|
||||||
INSTALL_COMMAND "")
|
INSTALL_COMMAND "")
|
||||||
|
|
||||||
# curses
|
# curses We pull this in because libsinsp won't build without it
|
||||||
# We pull this in because libsinsp won't build without it
|
|
||||||
set(CURSES_NEED_NCURSES TRUE)
|
set(CURSES_NEED_NCURSES TRUE)
|
||||||
find_package(Curses REQUIRED)
|
find_package(Curses REQUIRED)
|
||||||
message(STATUS "Found ncurses: include: ${CURSES_INCLUDE_DIR}, lib: ${CURSES_LIBRARIES}")
|
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
|
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")
|
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
|
# gRPC
|
||||||
include(gRPC)
|
include(gRPC)
|
||||||
|
|
||||||
@@ -236,8 +241,8 @@ add_subdirectory(rules)
|
|||||||
# Dockerfiles
|
# Dockerfiles
|
||||||
add_subdirectory(docker)
|
add_subdirectory(docker)
|
||||||
|
|
||||||
# Clang format
|
# Clang format add_custom_target(format COMMAND clang-format --style=file -i $<TARGET_PROPERTY:falco,SOURCES> COMMENT
|
||||||
# add_custom_target(format COMMAND clang-format --style=file -i $<TARGET_PROPERTY:falco,SOURCES> COMMENT "Formatting ..." VERBATIM)
|
# "Formatting ..." VERBATIM)
|
||||||
|
|
||||||
# Shared build variables
|
# Shared build variables
|
||||||
set(FALCO_SINSP_LIBRARY sinsp)
|
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)
|
set(FALCO_BIN_DIR bin)
|
||||||
|
|
||||||
add_subdirectory(scripts)
|
add_subdirectory(scripts)
|
||||||
|
add_subdirectory(userspace/profiler)
|
||||||
add_subdirectory(userspace/engine)
|
add_subdirectory(userspace/engine)
|
||||||
add_subdirectory(userspace/falco)
|
add_subdirectory(userspace/falco)
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
|
|||||||
@@ -12,16 +12,17 @@
|
|||||||
#
|
#
|
||||||
include(ExternalProject)
|
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
|
set(CATCH_EXTERNAL_URL URL https://github.com/catchorg/catch2/archive/v2.12.1.tar.gz URL_HASH
|
||||||
SHA256=e5635c082282ea518a8dd7ee89796c8026af8ea9068cd7402fb1615deacd91c3)
|
SHA256=e5635c082282ea518a8dd7ee89796c8026af8ea9068cd7402fb1615deacd91c3)
|
||||||
|
|
||||||
ExternalProject_Add(
|
ExternalProject_Add(
|
||||||
catch2
|
catch2
|
||||||
PREFIX ${CMAKE_BINARY_DIR}/catch2-prefix
|
PREFIX ${CATCH2_PREFIX}
|
||||||
${CATCH_EXTERNAL_URL}
|
${CATCH_EXTERNAL_URL}
|
||||||
CONFIGURE_COMMAND ""
|
CONFIGURE_COMMAND ""
|
||||||
BUILD_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)
|
${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
|
# 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
|
# 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
|
# License for the specific language governing permissions and limitations under
|
||||||
# the License.
|
# 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")
|
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}"
|
"${YAMLCPP_INCLUDE_DIR}"
|
||||||
"${CIVETWEB_INCLUDE_DIR}"
|
"${CIVETWEB_INCLUDE_DIR}"
|
||||||
"${PROJECT_SOURCE_DIR}/userspace/falco")
|
"${PROJECT_SOURCE_DIR}/userspace/falco")
|
||||||
add_dependencies(falco_test catch2)
|
add_dependencies(falco_test catch2 hedley)
|
||||||
|
|
||||||
include(CMakeParseArguments)
|
include(CMakeParseArguments)
|
||||||
include(CTest)
|
include(CTest)
|
||||||
include(Catch)
|
include(Catch)
|
||||||
catch_discover_tests(falco_test)
|
catch_discover_tests(falco_test)
|
||||||
separate_arguments(FALCO_TESTS_ARGUMENTS)
|
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()
|
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
|
falco
|
||||||
PUBLIC
|
PUBLIC
|
||||||
"${SYSDIG_SOURCE_DIR}/userspace/sysdig"
|
"${SYSDIG_SOURCE_DIR}/userspace/sysdig"
|
||||||
|
"${HEDLEY_INCLUDE}"
|
||||||
|
"${PROJECT_SOURCE_DIR}/userspace/profiler"
|
||||||
"${PROJECT_SOURCE_DIR}/userspace/engine"
|
"${PROJECT_SOURCE_DIR}/userspace/engine"
|
||||||
"${PROJECT_BINARY_DIR}/userspace/falco"
|
"${PROJECT_BINARY_DIR}/userspace/falco"
|
||||||
"${PROJECT_BINARY_DIR}/driver/src"
|
"${PROJECT_BINARY_DIR}/driver/src"
|
||||||
@@ -84,6 +86,7 @@ target_link_libraries(
|
|||||||
falco
|
falco
|
||||||
falco_engine
|
falco_engine
|
||||||
sinsp
|
sinsp
|
||||||
|
falco_profiler
|
||||||
"${GPR_LIB}"
|
"${GPR_LIB}"
|
||||||
"${GRPC_LIB}"
|
"${GRPC_LIB}"
|
||||||
"${GRPCPP_LIB}"
|
"${GRPCPP_LIB}"
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ limitations under the License.
|
|||||||
#include "statsfilewriter.h"
|
#include "statsfilewriter.h"
|
||||||
#include "webserver.h"
|
#include "webserver.h"
|
||||||
#include "grpc_server.h"
|
#include "grpc_server.h"
|
||||||
|
#include "profiler.h"
|
||||||
#include "banned.h" // This raises a compilation error when certain functions are used
|
#include "banned.h" // This raises a compilation error when certain functions are used
|
||||||
|
|
||||||
typedef function<void(sinsp* inspector)> open_t;
|
typedef function<void(sinsp* inspector)> open_t;
|
||||||
@@ -235,6 +236,7 @@ uint64_t do_inspect(falco_engine *engine,
|
|||||||
bool all_events,
|
bool all_events,
|
||||||
int &result)
|
int &result)
|
||||||
{
|
{
|
||||||
|
PROFILEME();
|
||||||
uint64_t num_evts = 0;
|
uint64_t num_evts = 0;
|
||||||
int32_t rc;
|
int32_t rc;
|
||||||
sinsp_evt* ev;
|
sinsp_evt* ev;
|
||||||
@@ -1291,6 +1293,7 @@ exit:
|
|||||||
//
|
//
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
alloc_chunk();
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
// g_restart will cause the falco loop to exit, but we
|
// 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