Debugging and profiling the engine - flutter-tizen/flutter-tizen Wiki

Remote debugging with GDB and gdbserver

The overall workflow is as described in Remote debugging with GDB and gdbserver. The only difference is that you have to manually build your engine and embedder from source.

  1. Build the engine and embedder from source (in either debug or release mode). Before running the build, modify the build/config/compiler/BUILD.gn file as follows to embed extra debug information (g2) in the output binaries.

    @@ -907,6 +903,6 @@ config("minimal_symbols") {
    
     config("no_symbols") {
       if (!is_win) {
    -    cflags = [ "-g0" ]
    +    cflags = [ "-g2" ]
       }
     }
    
  2. Once the build is done, copy stripped build artifacts into flutter-tizen's cached artifacts directory (flutter-tizen/flutter/bin/cache/artifacts/engine). You will also see unstripped binary files (so.unstripped) in the output directory. Those files will be used later.

  3. Build and install your app. Then, start remote debugging via gdbserver.

  4. Once GDB is connected to the gdbserver, find the base addresses of the engine and embedder using info share. In the following example, the addresses are 0xb53ee540 and 0xb74d7030.

    (gdb) info share
    0xb53ee540  0xb5fe6719  Yes (*)     target:/opt/usr/globalapps/com.example.my_app/bin/../lib/libflutter_engine.so
    0xb74d7030  0xb7537b79  Yes (*)     target:/opt/usr/globalapps/com.example.my_app/bin/../lib/libflutter_tizen_mobile.so
    
  5. Use the addresses to read symbol information from unstripped binaries in the host file system.

    (gdb) add-symbol-file /path/to/so.unstripped/libflutter_engine.debug 0xb53ee540
    (gdb) add-symbol-file /path/to/so.unstripped/libflutter_tizen_mobile.debug 0xb74d7030
    

    Note: If you're connecting from VS Code, you can run GDB commands through the VS Code Debug Console (-exec info share, -exec add-symbol-file).

Local debugging with LLDB

Note: LLDB is available for rooted devices (such as a mobile emulator) only.

  1. Download and install LLDB (or GDB) by referring to Installation.

  2. Also install debug packages for system libraries (e.g. ecore-core-debuginfo) as necessary.

  3. Build the engine and embedder from source and copy unstripped build artifacts (so.unstripped) into flutter-tizen's cached artifacts directory (flutter-tizen/flutter/bin/cache/artifacts/engine).

    Note: If you want to include extra debug information in the artifacts, see step 1 in the previous section. Make sure your device has enough disk space.

  4. Build and run your app.

  5. Attach LLDB to the running app.

    $ sdb root on
    $ sdb shell
    bash-3.2# lldb -p [PID]
    
  6. Load symbol data if you installed any debug package in step 2. For example, if you installed the ecore-core-debuginfo package, run

    (lldb) add-dsym /usr/lib/debug/usr/lib/libecore.so.1.25.0.debug
    

Profiling app startup with Linux perf

Note: You need a rooted device (such as a Raspberry Pi) to use perf. You can't use emulators for profiling purposes because Dart doesn't support x86 AOT.

Building perf

  1. Download the kernel source code for your target device (e.g. platform/kernel/linux-rpi for Raspberry Pi 4) and checkout a proper branch.

  2. Locate the perf tool (tools/perf) in the kernel source code. We will cross build it via Docker ARM emulation. You can alternatively use GBS if you're familiar with it.

  3. Install Docker and QEMU packages on your host machine by referring to Install Docker Engine on Ubuntu and Running and Building ARM Docker Containers on x86.

  4. Run a Docker container of your device's architecture. For example, if your device is an arm64 Raspberry Pi, run

    docker run --rm -ti --platform linux/arm64 -v [path_to_repo]:/linux-rpi arm64v8/debian:buster bash
    
  5. Build perf by running the following commands.

    apt update
    apt install make gcc bison flex
    apt install libelf-dev libdw-dev systemtap-sdt-dev libunwind-dev \
                libssl-dev python3 libiberty-dev libzstd-dev libcap-dev
    cd /linux-rpi/tools/perf
    make clean
    make
    
  6. Once the build is done, copy the output file (perf) to your device.

    sdb root on
    sdb push perf /home/owner/perf
    

Collecting perf data

  1. Build your app in release mode with unstripped engine and embedder artifacts (by referring to Debugging with lldb) and install to your device.

  2. Open the sdb shell and type the following commands as root. Set ELM_PROFILE to another value if you're not using the common profile.

    echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
    export ELM_ENGINE=wayland_egl
    export ELM_DISPLAY=wl
    export HOME=/opt/usr/home/owner
    export ELM_PROFILE=common
    export XDG_RUNTIME_DIR=/run/user/5001
    export DBUS_SESSION_BUS_ADDRESS="kernel:path=/sys/fs/kdbus/5001-user/bus;unix:path=/run/user/5001/bus"
    
  3. Launch your app under perf. For example, if your app is a native app and its app ID is io.flutter.demo.gallery, run

    cd /home/owner
    AUL_APPID=io.flutter.demo.gallery ./perf record -v -F 5000 -g /opt/usr/globalapps/io.flutter.demo.gallery/bin/runner
    

    Note: In case of a .NET application, replace /opt/usr/.../runner with dotnet-launcher --standalone /opt/usr/.../Runner.dll.

  4. Once the app appears on your screen, stop tracing by pressing Ctrl+C.

  5. Convert the raw sampling data (perf.data) into a script file.

    ./perf script > perf.script
    
  6. Copy the file to your host machine.

    sdb pull /home/owner/perf.script
    

Visualizing the perf data

The recorded perf data can be opened by various tools available online. Among them, the following two are recommended.

  1. Firefox Profiler

    Drag and drop the perf.script file into the Firefox Profiler web UI.

  2. FlameGraph

    Clone the repository and run the following commands to convert perf.script into a .svg file which can be opened by your browser.

    cat perf.script | ./stackcollapse-perf.pl > out.perf-folded
    cat out.perf-folded | ./flamegraph.pl > perf-kernel.svg
    

Tracing the engine with DevTools

Caution: This method is outdated as of Flutter 2.5. Native symbols will not be resolved correctly even if you apply the following or any other methods suggested in https://github.com/flutter-tizen/engine/issues/95.

You can skip steps 1-2 and 6-7 if you don't need native stack traces but only want to trace events from Dart code (framework).

  1. Build the engine from source in profile mode. Before running the build, make sure to enable the enable_profiling flag by applying this patch.

  2. Once the build is done, find unstripped build artifacts (so.unstripped) from the output directory, and copy them to flutter-tizen's cached artifacts directory (flutter-tizen/flutter/bin/cache/artifacts/engine).

  3. Build and run your app in profile mode.

    flutter-tizen run --profile
    
  4. Open DevTools and go to the Performance tab.

  5. Let your app draw some frames and click one of the colored bars displayed in the FPS chart.

  6. Click CPU Flame Chart in the below CPU Profile view. In the top right of the view, click the Filter icon (image) and make sure Hide Native code is unchecked.

  7. Check if native symbols are displayed correctly in the CPU Flame Chart.

  8. You can also use the CPU Profiler tab if you want to profile the app only for a specific duration. See Flutter Docs: CPU Profiler view for details.