easy_profiler

repository·develop·Indexed 25 days ago

https://github.com/yse/easy_profiler

A lightweight, cross-platform C++ library for high-performance profiling with low overhead. It provides tools to instrument functions and code blocks, capture thread context switches, and visualize data via a dedicated GUI. Supports real-time network streaming, file dumping, and includes CLI tools like easy_profiler_converter for JSON conversion and a reader utility for processing profiling data.

Tokens
2.6K
Snippets
6
Records
11
Agent score
82%

What's inside easy_profiler

  1. Collect profiling data by dumping to a file

    develop

    To save profiling data to a file:

    1. Enable profiling in your code using the EASY_PROFILER_ENABLE macro.
    2. Call profiler::dumpBlocksToFile("filename.prof") at the desired point in your execution.
    void main() {
        EASY_PROFILER_ENABLE;
        profiler::dumpBlocksToFile("test_profile.prof");
    }
  2. Collect profiling data via network streaming

    develop

    Streaming is the preferred method for real-time profiling.

    1. In your application, call profiler::startListen(). This opens port 28077 to listen for signals from the profiler_gui.
    2. In the profiler_gui, connect using the application's hostname or IP address.
    3. Use the GUI to 'Start capture' and 'Stop capture'.

    To profile application startup, ensure you include the EASY_PROFILER_ENABLE macro alongside profiler::startListen().

    void main() {
        profiler::startListen();
        /* do work */
    }
  3. Capture thread context-switch events

    develop

    To capture system context-switch events (including duration, target thread ID, owner process ID, and owner process name), specific OS permissions are required:

    • Windows: Launch your application as an Administrator.
    • Linux: Use a systemtap script with root privileges. Example for Fedora:
    #stap -o /tmp/cs_profiling_info.log scripts/context_switch_logger.stp name APPLICATION_NAME

    Replace APPLICATION_NAME with your application's name.

  4. Integrate easy_profiler using CMake

    develop

    To integrate easy_profiler into a CMake project, set CMAKE_PREFIX_PATH to the directory containing the lib/cmake/easy_profiler folder from the release package. Then, use find_package(easy_profiler REQUIRED) and link your target with easy_profiler using target_link_libraries.

    project(my_application)
    
    set(SOURCES
        main.cpp
    )
    
    # CMAKE_PREFIX_PATH should be set to <easy_profiler-release_dir>/lib/cmake/easy_profiler
    find_package(easy_profiler REQUIRED)  # STEP 1 #########################
    
    add_executable(my_application ${SOURCES})
    
    target_link_libraries(my_application easy_profiler)  # STEP 2 ##########
  5. Build easy_profiler

    develop

    Prerequisites

    • CMake 3.0 or higher
    • C++11 compatible compiler
    • For GUI: Qt 5.3.0 or higher

    Linux

    $ mkdir build
    $ cd build
    $ cmake -DCMAKE_BUILD_TYPE="Release" ..
    $ make

    MacOS

    $ mkdir build
    $ cd build
    $ cmake -DCMAKE_CXX_COMPILER=g++-5 -DCMAKE_C_COMPILER=gcc-5 -DCMAKE_BUILD_TYPE="Release" ..
    $ make

    Windows (using CMake generator)

    Specify the path to your Qt CMake scripts using CMAKE_PREFIX_PATH. For example, for a Visual Studio 2013 Win64 build with Qt 6:

    $ mkdir build
    $ cd build
    $ cmake -DCMAKE_PREFIX_PATH="C:\Qt\6.7.2\msvc2013_64\lib\cmake" .. -G "Visual Studio 12 2013 Win64"
  6. Insert profiling blocks and functions

    develop

    Use macros to instrument your code. EASY_FUNCTION profiles an entire function, while EASY_BLOCK profiles a specific scope. Blocks can be colored using profiler::colors or custom ARGB values. Scoped blocks (using EASY_BLOCK without a manual EASY_END_BLOCK) are automatically closed when the scope ends via destructor.

    #include <easy/profiler.h>
    
    void foo() {
        EASY_FUNCTION(profiler::colors::Magenta); // Magenta block with name "foo"
    
        EASY_BLOCK("Calculating sum"); // Begin block with default color == Amber100
        int sum = 0;
        for (int i = 0; i < 10; ++i) {
            EASY_BLOCK("Addition", profiler::colors::Red); // Scoped red block (no EASY_END_BLOCK needed)
            sum += i;
        }
        EASY_END_BLOCK; // End of "Calculating sum" block
    
        EASY_BLOCK("Calculating multiplication", profiler::colors::Blue500); // Blue block
        int mul = 1;
        for (int i = 1; i < 11; ++i)
            mul *= i;
        //EASY_END_BLOCK; // This is not needed because all blocks are ended on destructor when closing braces met
    }
    
    void bar() {
        EASY_FUNCTION(0xfff080aa); // Function block with custom ARGB color
    }
    
    void baz() {
        EASY_FUNCTION(); // Function block with default color == Amber100
    }
  7. Store variables for profiling

    develop

    You can capture variable values and arrays during profiling using EASY_VALUE and EASY_ARRAY. These are defined in <easy/arbitrary_value.h>. To ensure a variable's identity remains consistent even if its address changes (e.g., a local variable in a loop), use the EASY_VIN macro.

    #include <easy/profiler.h>
    #include <easy/arbitrary_value.h> // EASY_VALUE, EASY_ARRAY are defined here
    
    class Object {
        Vector3 m_position; // Let's suppose Vector3 is a struct { float x, y, z; };
        unsigned int  m_id;
    public:
        void act() {
            EASY_FUNCTION(profiler::colors::Cyan);
    
            // Dump variables values
            constexpr auto Size = sizeof(Vector3) / sizeof(float);
            EASY_VALUE("id", m_id);
            EASY_ARRAY("position", &m_position.x, Size, profiler::color::Red);
    
            // Do something ...
        }
    
        void loop(uint32_t N) {
            EASY_FUNCTION();
            EASY_VALUE("N", N, EASY_VIN("N")); /* EASY_VIN is used here to ensure
                                                that this value id will always be
                                                the same, because the address of N
                                                can change */
            for (uint32_t i = 0; i < N; ++i) {
                // Do something
            }
        }
    };
  8. Dump profiling blocks to a file with dumpBlocksToFile

    develop

    The profiler::dumpBlocksToFile function allows you to write the currently collected profiling blocks to a specified file.

    Parameters:

    • filename: C-string representing the destination path.

    Returns:

    • The number of blocks dumped to the file.
  9. Read profiling data using fillTreesFromFile

    develop

    The fillTreesFromFile function is used to parse a profiling file and populate various data structures representing the collected profiling information, including blocks, descriptors, and bookmarks.

    Parameters:

    • filename: C-string path to the input file.
    • beginEndTime: profiler::BeginEndTime object to store start/end timestamps.
    • serialized_blocks: profiler::SerializedData for serialized block data.
    • serialized_descriptors: profiler::SerializedData for serialized descriptor data.
    • descriptors: profiler::descriptors_list_t to be populated with descriptors.
    • blocks: profiler::blocks_t to be populated with blocks.
    • threaded_trees: profiler::thread_blocks_tree_t to be populated with the hierarchical tree structure of blocks per thread.
    • bookmarks: profiler::bookmarks_t to be populated with bookmarks.
    • descriptorsNumberInFile: Pointer/reference to uint32_t to store the count of descriptors found.
    • version: Pointer/reference to uint32_t to store the file version.
    • pid: Pointer/reference to profiler::processid_t to store the process ID.
    • is_verbose: Boolean flag for verbosity.
    • errorMessage: std::stringstream to capture error details if reading fails.

    Returns:

    • The number of blocks successfully read (as a uint32_t). If this returns 0, the operation failed, and the reason is available in errorMessage.
  10. Use the reader CLI to process profiling data

    develop

    The reader tool is a CLI utility used to read profiling data from a file and print information about the collected blocks to the terminal.

    Usage:

    Run the executable with the following arguments:

    1. filename: The path to the input .prof file containing profiling data.
    2. dump_filename (optional): The path where a new profiling file will be dumped. If provided, the tool will enable profiling and dump the reader's own profiling data to this file.

    If arguments are not provided via the command line, the tool will prompt you for the input filename and the output filename via standard input.

  11. Use the easy_profiler_converter CLI to convert profiling data

    develop

    The easy_profiler_converter is a command-line tool used to convert binary profiling data files into JSON format.

    Usage:

    ./easy_profiler_converter INPUT_PROF_FILE [OUTPUT_JSON_FILE]

    Arguments:

    • INPUT_PROF_FILE (Required): The path to the input profiling data file.
    • OUTPUT_JSON_FILE (Optional): The path where the converted JSON file will be saved. If this argument is omitted, the JSON output will be printed to stdout.