gst-shark安卓编译使用方法

gst-shark 是由 RidgeRun 公司开发的一款开源性能分析与调试工具集,它通过 GStreamer 内置的追踪(Tracing)子系统,在不修改管道代码的前提下,实时提取关键性能数据,帮助开发者快速定位多媒体处理管道中的性能瓶颈。

gst-shark基本介绍

九大追踪器(Tracers)

gst-shark 的核心是一系列可插拔的追踪器,每个都针对特定的性能分析场景。你可以通过 GST_TRACERS 环境变量按需启用。

追踪器名称 核心功能 典型用途
proctime 测量每个元素处理单个缓冲区所花费的时间 定位处理最慢的元素,是分析性能瓶颈的首选。
interlatency 测量缓冲区在管道不同点之间的累积延迟 分析延迟在哪个环节累积最多。
framerate 测量每秒通过源衬垫(src pad)的帧数 验证实际输出帧率是否达标,检测丢帧。
scheduletime 测量接收衬垫(sink pad)上两个连续缓冲区到达的时间间隔 判断数据供给是否均匀,检测上游是否"卡顿"。
cpuusage 测量每个 CPU 核心的使用率 发现 CPU 瓶颈,判断负载是否均衡。
graphic 记录当前管道的图形化表示 快速理解管道拓扑,验证 Caps 协商。
bitrate 测量当前流的比特率(bits/s) 监控编码输出码率是否稳定。
queuelevel 测量每个 queue 元素中排队的缓冲区数量 诊断管道"卡住"或抖动,判断是上游太慢还是下游太慢。
buffer 打印每个经过接收衬垫的缓冲区的详细信息(PTS, DTS, size等) 逐帧分析时间戳连续性,排查数据异常。

工作原理与使用流程

gst-shark 是一个 GStreamer 插件,它利用 GStreamer 的追踪钩子(tracing hooks)在关键代码路径上插入"探针",将采集到的数据整理后,以 CTF(Common Trace Format) 格式输出。

使用流程高度依赖环境变量,通常在启动 GStreamer 应用前设置:

  1. GST_TRACERS :指定要启用的追踪器,多个用分号 ; 隔开。例如:export GST_TRACERS="proctime;interlatency;framerate"。

  2. GST_DEBUG :启用追踪系统的调试日志,通常设为 GST_TRACER:7。

  3. GST_SHARK_LOCATION :指定追踪数据(datastream 文件)的保存目录。

运行管道后,数据会以 CTF 格式保存到 GST_SHARK_LOCATION 指定的目录中。

数据分析与可视化

gst-shark 采集的 datastream 文件是二进制的,需要配合 metadata 文件(描述数据结构的"说明书")才能被解析。针对不同需求,有多种分析工具:

  • gstshark-plot(官方可视化) :一套 Octave 脚本 ,可将 CTF 数据渲染成直观的图表(PNG 或 PDF 格式)。需要安装 octave 和 babeltrace 等依赖。

  • babeltrace(命令行解析) :一个强大的命令行工具,能将 CTF 数据转换为人类可读的文本格式 ,方便用 grep 等工具进行快速过滤和排查。

  • Trace Compass(图形化深度分析) :一款功能强大的 Eclipse 插件,原生支持 CTF 格式,可提供时间轴、事件表、统计图表等多种视图,适合进行复杂的交互式分析。

gst-shark编译使用

编译环境:

Ubuntu 20.04.6 LTS

gstshark地址:https://github.com/RidgeRun/gst-shark

gstreamer地址:https://github.com/GStreamer/cerbero

gstreamer-common地址:https://github.com/GStreamer/common

本案例使用gstreamer的cerbero项目源码来编译gstreamer的库(应该也可以采用官方提供的gsteamer的版本包)。

gstshark源码编译

(1)github下载源码,搭建编译环境。其中gst-shark/common可能下载失败,删除目录单独下载

gstreamer-common地址:https://github.com/GStreamer/common。

(2)配置编译环境,使用NDK搭建交叉编译环境,主要是配置NDK工具链以及gstreamer的源码目录。

生成产物:libgstsharktracers.so和libgstshark.so

bash 复制代码
#!/bin/bash
set -e

# ############################################################################
#
#                       gst-shark Android 交叉编译脚本
#
#  ---------------------------------------------------------------------------
#   产物说明
#  ---------------------------------------------------------------------------
#   gst-shark 编译出来不是一个 .so, 而是两个:
#
#     1. libgst<插件名>.so      ← GStreamer 插件本体 (默认 libgstsharktracers.so)
#                                 里面含 gst_plugin_desc, GStreamer 会扫描加载它
#
#     2. libgst<依赖库名>.so    ← tracer 的公共实现库, 被插件本体依赖
#                                 默认 libgstshark.so, 不是插件
#
#   ★ 部署到 APK 时, 两个 .so 都必须拷贝, 只拷插件会 dlopen 失败。
#
#  ---------------------------------------------------------------------------
#   插件改名的关键规则 (GStreamer 插件加载机制)
#  ---------------------------------------------------------------------------
#   文件名                  内部注册名        导出符号
#   libgst<X>.so            <X>              gst_plugin_<X>_get_desc
#
#   GStreamer 从文件名 libgst<X>.so 推导符号 gst_plugin_<X>_get_desc,
#   所以文件名和内部注册名必须一致, 否则插件加载失败:
#     "Could not find symbol 'gst_plugin_<X>_get_desc'"
#
#   本脚本会自动同步:
#     PLUGIN_NAME=libgst<X>  ->  文件名 libgst<X>.so, 内部名 <X>
#
#  ---------------------------------------------------------------------------
#   编译产物安装位置
#  ---------------------------------------------------------------------------
#     <脚本目录>/out/<abi>/lib/gstreamer-1.0/
#         ├── libgst<插件名>.so    ← 插件本体 (可通过参数改)
#         └── libgst<依赖库名>.so  ← 依赖库 (可通过参数改, 默认不变)
#
#  ---------------------------------------------------------------------------
#   用法
#  ---------------------------------------------------------------------------
#     ./build.sh armv7                                   编译 armv7 (默认名)
#     ./build.sh arm64                                   编译 arm64 (默认名)
#     ./build.sh arm64 libgstmyplugin                    插件改名 libgstmyplugin.so
#     ./build.sh arm64 libgstmyplugin libmycore          插件 + 依赖库同时改名
#     ./build.sh clean                                   清理源码中间文件, 保留 out/
#
# ############################################################################


# ============================================================================
#    配置区
# ============================================================================

# ----------------------------------------------------------------------------
# 1. 插件名 (文件名 + 内部注册名会同步修改)
# ----------------------------------------------------------------------------
# 必须以 libgst 开头, 否则 GStreamer 不会扫描加载。
# 内部注册名会自动从 libgst<X> 推导为 <X>, 保证导出符号 gst_plugin_<X>_get_desc
# 与文件名匹配。
#
# 请避免使用和官方 GStreamer 插件重名的名字,否则可能覆盖系统插件或引起注册冲突。
PLUGIN_NAME_DEFAULT="libgstsharktracers"


# ----------------------------------------------------------------------------
# 2. Android NDK
# ----------------------------------------------------------------------------
NDK_PATH_DEFAULT="/home/user/cerbero/build/android-ndk-21"


# ----------------------------------------------------------------------------
# 3. GStreamer 依赖库路径 (Cerbero 输出)
# ----------------------------------------------------------------------------
CERBERO_DIST_DEFAULT="/home/user/cerbero/build/dist"

DEP_ARCH_ARMV7_DEFAULT="android_armv7"
DEP_ARCH_ARM64_DEFAULT="android_arm64"


# ----------------------------------------------------------------------------
# 4. Android API level
# ----------------------------------------------------------------------------
API_DEFAULT=21


# ----------------------------------------------------------------------------
# 5. 交叉编译工具链参数
# ----------------------------------------------------------------------------
ARCH_ARMV7_TARGET="armv7a-linux-androideabi"
ARCH_ARMV7_HOST="arm-linux-androideabi"
ARCH_ARMV7_ABI="armeabi-v7a"

ARCH_ARM64_TARGET="aarch64-linux-android"
ARCH_ARM64_HOST="aarch64-linux-android"
ARCH_ARM64_ABI="arm64-v8a"


# ----------------------------------------------------------------------------
# 6. 依赖库文件名 (默认 libgstshark)
# ----------------------------------------------------------------------------
SHARK_LIB_NAME_DEFAULT="libgstshark"


# ============================================================================
#    以下是脚本逻辑, 一般不需要修改
# ============================================================================

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR"

# ---------- 合并配置 ----------
PLUGIN_NAME="${PLUGIN_NAME:-$PLUGIN_NAME_DEFAULT}"
SHARK_LIB_NAME="${SHARK_LIB_NAME:-$SHARK_LIB_NAME_DEFAULT}"
NDK_PATH="${NDK_PATH:-$NDK_PATH_DEFAULT}"
CERBERO_DIST="${CERBERO_DIST:-$CERBERO_DIST_DEFAULT}"
API="${API:-$API_DEFAULT}"

DEP_ARCH_ARMV7="${DEP_ARCH_ARMV7:-$DEP_ARCH_ARMV7_DEFAULT}"
DEP_ARCH_ARM64="${DEP_ARCH_ARM64:-$DEP_ARCH_ARM64_DEFAULT}"

TOOLCHAIN="$NDK_PATH/toolchains/llvm/prebuilt/linux-x86_64"
MAKEFILE_AM="plugins/tracers/Makefile.am"
PLUGIN_C="plugins/tracers/gstplugin.c"

BUILD_FILES_FIND=(
  find . -path "./out" -prune -o -type f
  \( -name "Makefile.am" -o -name "*.mak" -o -name "configure.ac" \) -print
)

# ============================================================================
# 名字合法性校验
# ============================================================================
validate_name() {
  local kind="$1"
  local name="$2"

  if [ -z "$name" ]; then
    echo "ERROR: $kind 名字为空"
    exit 1
  fi

  if ! echo "$name" | grep -qE '^lib[A-Za-z][A-Za-z0-9_]*$'; then
    echo "ERROR: $kind 名字非法: '$name'"
    echo "       规则: 以 lib 开头, 后面第一个字符是字母,"
    echo "             之后只允许字母/数字/下划线"
    echo "       合法示例: libgstsharktracers / libgstmyplugin / libmycore"
    echo "       非法示例: libgst.so / 1libfoo / my-lib"
    exit 1
  fi
}

# ============================================================================
# 清理函数: 只清源码目录, 不动 out/
# ============================================================================
clean_build() {
  echo "==> 清理源码目录中间文件 (保留 out/) ..."

  if [ -f Makefile ]; then
    make distclean >/dev/null 2>&1 || true
  fi

  rm -rf config.status config.log autom4te.cache
  rm -f config.h stamp-h1 libtool

  find . -path "./out" -prune -o -type f -name "Makefile" -print \
    -exec rm -f {} + 2>/dev/null || true

  find . -path "./out" -prune -o -type d \( -name ".libs" -o -name ".deps" \) \
    -exec rm -rf {} + 2>/dev/null || true

  find . -path "./out" -prune -o -type f \
    \( -name "*.o" -o -name "*.lo" -o -name "*.la" -o -name "*.lai" \
       -o -name "*.exp" -o -name "*.ver" \) \
    -print -exec rm -f {} + 2>/dev/null || true

  find . -path "./out" -prune -o -type f -name "*.so*" \
    -print -exec rm -f {} + 2>/dev/null || true

  echo "==> 清理完成 (out/ 未受影响)"
}

# ============================================================================
# 改名: 插件本体
#   同步修改两处:
#     1) Makefile.am 里的 libgst<X> (文件名)
#     2) gstplugin.c 里 GST_PLUGIN_DEFINE 第三个参数 (内部注册名)
#
#   规则: PLUGIN_NAME=libgst<X>  ->  内部名 = <X>
#         保证 libgst<X>.so 里导出的符号是 gst_plugin_<X>_get_desc
#
#   幂等: 从当前状态改到目标状态, 反复跑安全
# ============================================================================
rename_plugin() {
  echo "==> 插件改名: $PLUGIN_NAME (文件名 + 内部注册名同步修改)"

  [ -f "$MAKEFILE_AM" ] || { echo "ERROR: 找不到 $MAKEFILE_AM"; exit 1; }
  [ -f "$PLUGIN_C" ]    || { echo "ERROR: 找不到 $PLUGIN_C";    exit 1; }

  # ---------- 推导目标内部名 ----------
  # libgst<X> -> <X>
  local TARGET_INTERNAL="${PLUGIN_NAME#libgst}"
  if [ -z "$TARGET_INTERNAL" ] || [ "$TARGET_INTERNAL" = "$PLUGIN_NAME" ]; then
    echo "ERROR: 无法从 '$PLUGIN_NAME' 推导内部名 (必须以 libgst 开头)"
    exit 1
  fi
  echo "    目标文件名: $PLUGIN_NAME.so"
  echo "    目标内部名: $TARGET_INTERNAL"

  # ---------- 1. Makefile.am: 替换文件名 ----------
  local CURRENT_NAME
  CURRENT_NAME=$(grep -oE '^plugin_LTLIBRARIES *= *lib[A-Za-z][A-Za-z0-9_]*' "$MAKEFILE_AM" \
                 | head -1 | sed 's/.*= *//')

  if [ -z "$CURRENT_NAME" ]; then
    echo "ERROR: 无法从 $MAKEFILE_AM 解析出插件名"
    echo "       当前 plugin_LTLIBRARIES 行:"
    grep -n "plugin_LTLIBRARIES" "$MAKEFILE_AM" || true
    echo "       修复: git checkout plugins/tracers/Makefile.am"
    exit 1
  fi

  echo "    当前文件名: $CURRENT_NAME.so"

  if [ "$CURRENT_NAME" != "$PLUGIN_NAME" ]; then
    while IFS= read -r f; do
      if grep -q "$CURRENT_NAME" "$f" 2>/dev/null; then
        sed -i "s/${CURRENT_NAME}/${PLUGIN_NAME}/g" "$f"
        echo "    更新文件名: $f"
      fi
    done < <("${BUILD_FILES_FIND[@]}")
  else
    echo "    文件名已是目标名, 跳过"
  fi

  # ---------- 2. gstplugin.c: 跨行匹配 GST_PLUGIN_DEFINE 第三个参数 ----------
  # 宏结构: GST_PLUGIN_DEFINE(major, minor, name, description, init, ...)
  #         第三个参数 name 就是内部注册名
  # 用 perl -0777 把整个文件当一行处理, 正则用 \s 跨行匹配
  local CURRENT_INTERNAL
  CURRENT_INTERNAL=$(perl -0777 -ne '
    if (/GST_PLUGIN_DEFINE\s*\(\s*[^,]+,\s*[^,]+,\s*([a-zA-Z_][a-zA-Z0-9_]*)/s) {
      print $1;
    }
  ' "$PLUGIN_C")

  if [ -z "$CURRENT_INTERNAL" ]; then
    echo "ERROR: 无法从 $PLUGIN_C 解析出 GST_PLUGIN_DEFINE 的内部名"
    echo "       当前 GST_PLUGIN_DEFINE 附近内容:"
    grep -A 3 "GST_PLUGIN_DEFINE" "$PLUGIN_C" | head -8 || true
    exit 1
  fi

  echo "    当前内部名: $CURRENT_INTERNAL"

  if [ "$CURRENT_INTERNAL" != "$TARGET_INTERNAL" ]; then
    # 只替换第三个参数, 其余内容 (描述字符串、init、version 等) 完全不动
    perl -0777 -i -pe '
      s/(GST_PLUGIN_DEFINE\s*\(\s*[^,]+,\s*[^,]+,\s*)[a-zA-Z_][a-zA-Z0-9_]*/${1}'"${TARGET_INTERNAL}"'/s
    ' "$PLUGIN_C"
    echo "    更新内部名: $CURRENT_INTERNAL -> $TARGET_INTERNAL"
  else
    echo "    内部名已是目标名, 跳过"
  fi

  # ---------- 3. 验证 ----------
  echo "    验证 Makefile.am:"
  grep -n "plugin_LTLIBRARIES" "$MAKEFILE_AM" || true
  echo "    验证 gstplugin.c:"
  grep -A 3 "GST_PLUGIN_DEFINE" "$PLUGIN_C" | head -5
}

# ============================================================================
# 改名: 依赖库 (libgstshark)
#   必须在 rename_plugin 之后调用
# ============================================================================
rename_shark_lib() {
  echo "==> 依赖库改名: $SHARK_LIB_NAME (内部库不是插件)"

  [ -f "$MAKEFILE_AM" ] || { echo "ERROR: 找不到 $MAKEFILE_AM"; exit 1; }

  local CURRENT_LIB
  CURRENT_LIB=$(grep -oE '^noinst_LTLIBRARIES *= *lib[A-Za-z][A-Za-z0-9_]*' "$MAKEFILE_AM" \
                | head -1 | sed 's/.*= *//')

  if [ -z "$CURRENT_LIB" ]; then
    CURRENT_LIB=$(grep -oE '^lib_LTLIBRARIES *= *lib[A-Za-z][A-Za-z0-9_]*' "$MAKEFILE_AM" \
                  | head -1 | sed 's/.*= *//')
  fi

  if [ -z "$CURRENT_LIB" ]; then
    local PLUGIN_LA
    PLUGIN_LA=$(grep -oE '^plugin_LTLIBRARIES *= *lib[A-Za-z][A-Za-z0-9_]*' "$MAKEFILE_AM" \
                | head -1 | sed 's/.*= *//')
    CURRENT_LIB=$(grep -oE '^lib[A-Za-z][A-Za-z0-9_]*_la_SOURCES' "$MAKEFILE_AM" \
                  | sed 's/_la_SOURCES//' \
                  | grep -v "^${PLUGIN_LA}$" \
                  | head -1)
  fi

  if [ -z "$CURRENT_LIB" ]; then
    echo "ERROR: 无法从 $MAKEFILE_AM 解析出依赖库名"
    grep -n "LTLIBRARIES\|_la_SOURCES" "$MAKEFILE_AM" | head || true
    exit 1
  fi

  echo "    当前: $CURRENT_LIB  ->  目标: $SHARK_LIB_NAME"
  if [ "$CURRENT_LIB" = "$SHARK_LIB_NAME" ]; then
    echo "    已是目标名, 跳过"
    return
  fi

  local updated=0
  while IFS= read -r f; do
    if grep -q "$CURRENT_LIB" "$f" 2>/dev/null; then
      sed -i -E "s/${CURRENT_LIB}([^a-zA-Z0-9]|$)/${SHARK_LIB_NAME}\1/g" "$f"
      echo "    更新: $f"
      updated=$((updated + 1))
    fi
  done < <("${BUILD_FILES_FIND[@]}")

  echo "    共更新 $updated 个文件"
}

# ============================================================================
# clean 子命令
# ============================================================================
if [ "$1" = "clean" ]; then
  clean_build
  echo
  echo "out/ 目录内容:"
  ls -R "$SCRIPT_DIR/out" 2>/dev/null || echo "  (空)"
  exit 0
fi

# ============================================================================
# 参数解析
# ============================================================================
ARCH=${1:-armv7}
if [ -n "$2" ]; then
  PLUGIN_NAME="$2"
fi
if [ -n "$3" ]; then
  SHARK_LIB_NAME="$3"
fi

case "$ARCH" in
  armv7)
    TARGET="$ARCH_ARMV7_TARGET"
    HOST="$ARCH_ARMV7_HOST"
    ANDROID_ABI="$ARCH_ARMV7_ABI"
    DEP_ARCH="$DEP_ARCH_ARMV7"
    ;;
  arm64)
    TARGET="$ARCH_ARM64_TARGET"
    HOST="$ARCH_ARM64_HOST"
    ANDROID_ABI="$ARCH_ARM64_ABI"
    DEP_ARCH="$DEP_ARCH_ARM64"
    ;;
  *)
    echo "用法: $0 [armv7|arm64|clean] [插件文件名] [依赖库文件名]"
    echo "示例: $0 arm64 libgstmyplugin libmycore"
    exit 1
    ;;
esac

validate_name "插件"   "$PLUGIN_NAME"
validate_name "依赖库" "$SHARK_LIB_NAME"

# 插件名必须以 libgst 开头 (内部名推导需要)
if ! echo "$PLUGIN_NAME" | grep -qE '^libgst[A-Za-z][A-Za-z0-9_]*$'; then
  echo "ERROR: 插件名必须以 libgst 开头, 当前: '$PLUGIN_NAME'"
  echo "       合法示例: libgstsharktracers / libgstmyplugin"
  exit 1
fi

# ============================================================================
# 安装目录
# ============================================================================
OUT_DIR="$SCRIPT_DIR/out/$ANDROID_ABI"
PLUGIN_INSTALL_DIR="$OUT_DIR/lib/gstreamer-1.0"

rm -rf "$PLUGIN_INSTALL_DIR"
mkdir -p "$PLUGIN_INSTALL_DIR"
mkdir -p "$OUT_DIR/include"

# ============================================================================
# 工具链
# ============================================================================
export CC="$TOOLCHAIN/bin/${TARGET}${API}-clang"
export CXX="$TOOLCHAIN/bin/${TARGET}${API}-clang++"
export AR="$TOOLCHAIN/bin/llvm-ar"
export RANLIB="$TOOLCHAIN/bin/llvm-ranlib"
export STRIP="$TOOLCHAIN/bin/llvm-strip"
export PATH="$TOOLCHAIN/bin:$PATH"

# ============================================================================
# GStreamer 依赖
# ============================================================================
GSTREAMER_ROOT_ANDROID="$CERBERO_DIST/$DEP_ARCH"

export PKG_CONFIG_LIBDIR="$GSTREAMER_ROOT_ANDROID/lib/pkgconfig"
export PKG_CONFIG_PATH="$GSTREAMER_ROOT_ANDROID/lib/pkgconfig"

export LDFLAGS="${LDFLAGS} -L$GSTREAMER_ROOT_ANDROID/lib"
export LDFLAGS="${LDFLAGS} -Wl,-rpath-link,$GSTREAMER_ROOT_ANDROID/lib"
export LDFLAGS="${LDFLAGS} -lm"

# ============================================================================
# 前置检查
# ============================================================================
check_prereq() {
  local fail=0

  [ -x "$TOOLCHAIN/bin/${TARGET}${API}-clang" ] || {
    echo "ERROR: 找不到编译器 $TOOLCHAIN/bin/${TARGET}${API}-clang"
    fail=1
  }

  [ -f "$GSTREAMER_ROOT_ANDROID/lib/pkgconfig/gstreamer-1.0.pc" ] || {
    echo "ERROR: 找不到 $GSTREAMER_ROOT_ANDROID/lib/pkgconfig/gstreamer-1.0.pc"
    fail=1
  }

  [ -d "$GSTREAMER_ROOT_ANDROID/lib/gstreamer-1.0" ] || {
    echo "ERROR: 找不到 $GSTREAMER_ROOT_ANDROID/lib/gstreamer-1.0"
    fail=1
  }

  [ -f "$MAKEFILE_AM" ] || { echo "ERROR: 找不到 $MAKEFILE_AM"; fail=1; }
  [ -f "$PLUGIN_C" ]    || { echo "ERROR: 找不到 $PLUGIN_C";    fail=1; }

  [ "$fail" = "0" ] || exit 1
}

# ============================================================================
# 打印配置
# ============================================================================
print_config() {
  local TARGET_INTERNAL="${PLUGIN_NAME#libgst}"
  echo "============================================================"
  echo " 架构         : $ARCH ($ANDROID_ABI)"
  echo " 插件文件名   : $PLUGIN_NAME.so"
  echo " 插件内部名   : $TARGET_INTERNAL"
  echo " 导出符号     : gst_plugin_${TARGET_INTERNAL}_get_desc"
  echo " 依赖库文件名 : $SHARK_LIB_NAME.so"
  echo " TARGET       : $TARGET"
  echo " CC           : $CC"
  echo " NDK          : $NDK_PATH"
  echo " 依赖 GSt     : $GSTREAMER_ROOT_ANDROID"
  echo " 安装目录     : $PLUGIN_INSTALL_DIR"
  echo "============================================================"
}

# ============================================================================
# 主流程
# ============================================================================
check_prereq
print_config

echo "==> pkg-config 测试:"
pkg-config --cflags --libs gstreamer-1.0 || {
  echo "ERROR: pkg-config 找不到 gstreamer-1.0"
  exit 1
}

clean_build
rename_plugin        # 同步改 Makefile.am 和 gstplugin.c
rename_shark_lib     # 改依赖库名

./autogen.sh --host="$HOST" \
             --prefix="$OUT_DIR" \
             --libdir="$OUT_DIR/lib" \
             --disable-gtk-doc \
             --disable-graphviz \
             --disable-fatal-warnings \
             --disable-tests \
             --disable-examples

make -j"$(nproc)"

# ============================================================================
# 校验编译产物
# ============================================================================
SO="plugins/tracers/.libs/${PLUGIN_NAME}.so"
if [ ! -f "$SO" ]; then
  echo "ERROR: 插件编译产物不存在: $SO"
  ls -l plugins/tracers/.libs/ 2>/dev/null || true
  exit 1
fi
echo "==> 插件本体: $SO"
file "$SO"

# 校验导出符号是否匹配文件名
TARGET_INTERNAL="${PLUGIN_NAME#libgst}"
echo "==> 导出符号校验 (期望 gst_plugin_${TARGET_INTERNAL}_get_desc):"
"$TOOLCHAIN/bin/llvm-readelf" --dyn-syms "$SO" 2>/dev/null \
  | grep -E "gst_plugin_.*_get_desc" || echo "    (未找到)" 

# ============================================================================
# 安装
# ============================================================================
make install

find "$OUT_DIR/lib" -maxdepth 1 -name "${SHARK_LIB_NAME}*.so*" \
  -exec mv {} "$PLUGIN_INSTALL_DIR/" \; 2>/dev/null || true

INSTALLED_SO="$PLUGIN_INSTALL_DIR/${PLUGIN_NAME}.so"

if [ ! -f "$INSTALLED_SO" ]; then
  echo "==> 预期位置不存在, 全盘搜索实际落点 ..."
  FOUND=$(find "$OUT_DIR" -name "${PLUGIN_NAME}.so" -type f 2>/dev/null | head -1)
  if [ -n "$FOUND" ]; then
    echo "==> 实际位置: $FOUND"
    mv "$FOUND" "$INSTALLED_SO"
  else
    echo "ERROR: $OUT_DIR 下找不到 ${PLUGIN_NAME}.so"
    find "$OUT_DIR" -maxdepth 5 -print 2>/dev/null || true
    exit 1
  fi
fi

# ============================================================================
# 探测依赖
# ============================================================================
echo
echo "==> 插件本体的动态依赖 (与 $SHARK_LIB_NAME 相关):"
SHARK_DEPS=$( "$TOOLCHAIN/bin/llvm-readelf" -d "$INSTALLED_SO" 2>/dev/null \
              | grep NEEDED | grep -i "$(basename "$SHARK_LIB_NAME")" \
              | sed 's/.*\[\(.*\)\].*/\1/' ) || true

if [ -n "$SHARK_DEPS" ]; then
  echo "$SHARK_DEPS" | while read -r dep; do
    echo "    $dep"
  done
else
  echo "    (未检测到)"
fi

# ============================================================================
# 完成
# ============================================================================
echo
echo "============================================================"
echo " 安装完成: $INSTALLED_SO"
echo "============================================================"
file "$INSTALLED_SO"

if [ -n "$SHARK_DEPS" ]; then
  echo
  echo " 依赖库:"
  echo "$SHARK_DEPS" | while read -r dep; do
    LIB_PATH="$PLUGIN_INSTALL_DIR/$dep"
    if [ -f "$LIB_PATH" ]; then
      file "$LIB_PATH"
    fi
  done
fi

echo
echo "============================================================"
echo " APK 集成: 以下文件全部拷到"
echo "   app/src/main/jniLibs/$ANDROID_ABI/"
echo "============================================================"
find "$PLUGIN_INSTALL_DIR" -maxdepth 1 \( -name "*.so" -o -name "*.so.*" \) \
  -exec basename {} \; 2>/dev/null | sort | while read -r f; do
  echo "  $f"
done

gstshark集成使用

(1)将产物也就是插件库libgstsharktracers.so以及依赖libgstshark.so集成到我们的项目

(2)在gst_init前设置env环境值:

g_setenv("GST_TRACERS", "proctime;interlatency;framerate;cpuusage;bitrate;queuelevel;graphic", TRUE);

g_setenv("GST_SHARK_LOCATION", "/data/app", TRUE);

g_setenv("GST_DEBUG", "GST_TRACER:7", TRUE);

(3)运行程序抓取数据,GST_SHARK_LOCATION目录下生成datastream和metadata文件。

:datastream是追踪数据的输出文件。

:metadata是CTF 格式的schema/说明书,它描述的是 datastream 二进制文件的结构,用于解析datastream。

:graphic/pipeline0_2026-10-20_14:21:59.dot是设置**graphic抓取的管道dot。**

但是由于在安卓上兼容性的问题,所以无法抓取metadata的。可以采用在ubuntu上生成metadata

(4)ubuntu上生成metadata

(a)ubuntu安装同样版本的gstreamer环境。

bash 复制代码
# 1. 添加 PPA
sudo add-apt-repository ppa:tidewise/gstreamer-1.20
# 2. 更新软件源
sudo apt update
# 3. 升级 GStreamer
sudo apt upgrade
# 4. 验证版本(应显示 1.20.x)
gst-inspect-1.0 --version

(b)ubuntu编译安装gst-shark插件库(编译gst-shark源码,目标架构是ubuntu)。

bash 复制代码
#!/bin/bash
set -e

# ############################################################################
#
#                   gst-shark Ubuntu 主机编译与 metadata 生成脚本
#
#  ---------------------------------------------------------------------------
#   用途
#  ---------------------------------------------------------------------------
#   在 Ubuntu 主机上编译安装 gst-shark, 用于:
#     1. 生成 metadata 模板 (Android 上无法自动生成)
#     2. 与 Android 采集的 datastream 配对, 用 gstshark-plot / Trace Compass 分析
#
#  ---------------------------------------------------------------------------
#   产物
#  ---------------------------------------------------------------------------
#   安装到系统:
#     /usr/lib/<arch>/gstreamer-1.0/libgstsharktracers.so     ← 插件
#     /usr/lib/<arch>/libgstshark-0.9.so.0                    ← 依赖库
#
#   metadata 模板:
#     <脚本目录>/gstshark_meta/metadata
#
#   与 Android datastream 配对目录:
#     <脚本目录>/tracing_data/
#         ├── datastream   (从 Android 拷贝)
#         └── metadata     (本脚本生成, UUID 已对齐)
#
#  ---------------------------------------------------------------------------
#   用法
#  ---------------------------------------------------------------------------
#     ./build-ubuntu.sh                         编译安装并生成 metadata
#     ./build-ubuntu.sh --skip-deps             跳过依赖安装
#     ./build-ubuntu.sh --android-datastream <path>
#                                               编译后直接与 Android datastream 配对
#     ./build-ubuntu.sh --plot                  生成 metadata 后自动运行 gstshark-plot
#     ./build-ubuntu.sh clean                   清理编译中间文件
#
# ############################################################################


# ============================================================================
#    配置区
# ============================================================================

# tracer 列表, 必须和 Android 端完全一致
TRACERS_DEFAULT="proctime;interlatency;framerate;cpuusage;bitrate;queuelevel"

# 安装路径
PREFIX_DEFAULT="/usr"

# GStreamer API 版本
GST_API_VERSION="1.0"

# Android datastream 路径 (可选, 通过 --android-datastream 覆盖)
ANDROID_DATASTREAM=""


# ============================================================================
#    脚本逻辑
# ============================================================================

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR"

# ---------- 参数解析 ----------
SKIP_DEPS=0
RUN_PLOT=0

while [ $# -gt 0 ]; do
  case "$1" in
    --skip-deps)
      SKIP_DEPS=1
      shift
      ;;
    --android-datastream)
      ANDROID_DATASTREAM="$2"
      shift 2
      ;;
    --plot)
      RUN_PLOT=1
      shift
      ;;
    clean)
      echo "==> 清理源码目录中间文件 ..."
      make distclean 2>/dev/null || true
      rm -rf config.status config.log autom4te.cache
      rm -f config.h stamp-h1 libtool
      find . -path ./out -prune -o -type f -name "Makefile" -print -exec rm -f {} + 2>/dev/null || true
      find . -path ./out -prune -o -type d \( -name ".libs" -o -name ".deps" \) -exec rm -rf {} + 2>/dev/null || true
      find . -path ./out -prune -o -type f \( -name "*.o" -o -name "*.lo" -o -name "*.la" -o -name "*.lai" \) -delete 2>/dev/null || true
      echo "==> 清理完成 (out/ 保留)"
      exit 0
      ;;
    -h|--help)
      sed -n '3,40p' "$0"
      exit 0
      ;;
    *)
      echo "未知参数: $1"
      echo "用法: $0 [--skip-deps] [--android-datastream <path>] [--plot] [clean]"
      exit 1
      ;;
  esac
done

TRACERS="${TRACERS:-$TRACERS_DEFAULT}"
PREFIX="${PREFIX:-$PREFIX_DEFAULT}"


# ============================================================================
# 1. 检查源码
# ============================================================================
echo "==> [1/8] 检查源码 ..."

MAKEFILE_AM="plugins/tracers/Makefile.am"
PLUGIN_C="plugins/tracers/gstplugin.c"

[ -f "$MAKEFILE_AM" ] || { echo "ERROR: 找不到 $MAKEFILE_AM"; exit 1; }
[ -f "$PLUGIN_C" ]    || { echo "ERROR: 找不到 $PLUGIN_C";    exit 1; }

# 如果有 git, 恢复到干净状态, 避免 Android 编译时的改名残留
if [ -d .git ]; then
  if ! git diff --quiet 2>/dev/null; then
    echo "    检测到源码有未提交改动, 尝试 git checkout 恢复 ..."
    git checkout . 2>/dev/null || true
  fi
fi

# 二次确认插件名和内部名
PLUGIN_NAME=$(grep -oE '^plugin_LTLIBRARIES *= *lib[A-Za-z][A-Za-z0-9_]*' "$MAKEFILE_AM" \
              | head -1 | sed 's/.*= *//')
PLUGIN_INTERNAL=$(perl -0777 -ne '
  if (/GST_PLUGIN_DEFINE\s*\(\s*[^,]+,\s*[^,]+,\s*([a-zA-Z_][a-zA-Z0-9_]*)/s) {
    print $1;
  }
' "$PLUGIN_C")

echo "    插件文件名: $PLUGIN_NAME.so"
echo "    插件内部名: $PLUGIN_INTERNAL"

if [ "$PLUGIN_INTERNAL" != "${PLUGIN_NAME#libgst}" ]; then
  echo "ERROR: 文件名和内部名不匹配"
  echo "       文件名推导内部名: ${PLUGIN_NAME#libgst}"
  echo "       gstplugin.c 实际: $PLUGIN_INTERNAL"
  echo "       修复: git checkout ."
  exit 1
fi


# ============================================================================
# 2. 安装依赖
# ============================================================================
echo
echo "==> [2/8] 检查依赖 ..."

if [ "$SKIP_DEPS" = "0" ]; then
  MISSING=""
  command -v gcc             >/dev/null 2>&1 || MISSING="$MISSING gcc"
  command -v make            >/dev/null 2>&1 || MISSING="$MISSING make"
  command -v autoconf        >/dev/null 2>&1 || MISSING="$MISSING autoconf"
  command -v automake        >/dev/null 2>&1 || MISSING="$MISSING automake"
  command -v libtoolize      >/dev/null 2>&1 || MISSING="$MISSING libtool"
  command -v pkg-config      >/dev/null 2>&1 || MISSING="$MISSING pkg-config"
  command -v gst-inspect-1.0 >/dev/null 2>&1 || MISSING="$MISSING gstreamer1.0-tools"

  if [ -n "$MISSING" ]; then
    echo "    缺少: $MISSING"
    echo "    安装中 (需要 sudo) ..."
    sudo apt update
    sudo apt install -y \
      git build-essential \
      autoconf automake libtool pkg-config \
      gstreamer1.0-tools \
      libgstreamer1.0-dev \
      libgstreamer-plugins-base1.0-dev \
      libgstreamer-plugins-bad1.0-dev \
      graphviz libgraphviz-dev \
      gtk-doc-tools \
      babeltrace \
      octave epstool
  else
    echo "    基本工具已安装"
    # 检查开发库
    if ! pkg-config --exists gstreamer-1.0; then
      echo "    缺少 libgstreamer1.0-dev, 安装中 ..."
      sudo apt install -y libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
    fi
    if ! command -v babeltrace >/dev/null 2>&1; then
      echo "    缺少 babeltrace, 安装中 ..."
      sudo apt install -y babeltrace
    fi
  fi
else
  echo "    --skip-deps, 跳过依赖安装"
fi

# 确认 GStreamer 版本
GST_VERSION=$(pkg-config --modversion gstreamer-1.0 2>/dev/null || echo "unknown")
echo "    GStreamer 版本: $GST_VERSION"

# 简单版本检查
GST_MAJOR=$(echo "$GST_VERSION" | cut -d. -f1)
GST_MINOR=$(echo "$GST_VERSION" | cut -d. -f2)
if [ "$GST_MAJOR" = "1" ] && [ "$GST_MINOR" -lt 7 ]; then
  echo "ERROR: GStreamer 版本过低 (需要 >= 1.7.1)"
  exit 1
fi


# ============================================================================
# 3. 清理旧配置
# ============================================================================
echo
echo "==> [3/8] 清理旧配置 ..."

make distclean >/dev/null 2>&1 || true
rm -rf config.status config.log autom4te.cache
rm -f config.h stamp-h1 libtool
find . -path ./out -prune -o -type f -name "Makefile" -print -exec rm -f {} + 2>/dev/null || true

echo "    完成 (out/ 保留)"


# ============================================================================
# 4. 配置
# ============================================================================
echo
echo "==> [4/8] 配置 ..."

LIBDIR=$(pkg-config --variable=libdir gstreamer-1.0)
echo "    安装 prefix : $PREFIX"
echo "    安装 libdir : $LIBDIR"

./autogen.sh \
  --host="$(gcc -dumpmachine)" \
  --prefix="$PREFIX" \
  --libdir="$LIBDIR" \
  --disable-gtk-doc \
  --disable-fatal-warnings \
  --disable-tests \
  --disable-examples

echo "    配置完成"


# ============================================================================
# 5. 编译安装
# ============================================================================
echo
echo "==> [5/8] 编译 ..."

make -j"$(nproc)" ERROR_CFLAGS= || {
  echo "ERROR: 编译失败"
  exit 1
}

echo "    编译产物:"
ls -l plugins/tracers/.libs/*.so* 2>/dev/null | sed 's/^/      /'

echo
echo "==> [6/8] 安装 (需要 sudo) ..."

sudo make install
sudo ldconfig

echo "    安装完成"
echo "    插件路径: $LIBDIR/gstreamer-$GST_API_VERSION/libgstsharktracers.so"


# ============================================================================
# 6. 验证安装
# ============================================================================
echo
echo "==> [7/8] 验证安装 ..."

# 清 GStreamer 插件缓存
rm -rf "$HOME/.cache/gstreamer-1.0/" 2>/dev/null || true

if gst-inspect-1.0 sharktracers >/dev/null 2>&1; then
  echo "    gst-inspect-1.0 sharktracers 验证成功"
  echo "    可用 tracer:"
  gst-inspect-1.0 sharktracers 2>/dev/null | grep -E "^  \+" | sed 's/^/      /'
else
  echo "ERROR: gst-inspect-1.0 sharktracers 找不到插件"
  echo "       检查:"
  echo "         ls -l $LIBDIR/gstreamer-$GST_API_VERSION/libgstsharktracers.so"
  echo "         ldd  $LIBDIR/gstreamer-$GST_API_VERSION/libgstsharktracers.so"
  exit 1
fi


# ============================================================================
# 7. 生成 metadata
# ============================================================================
echo
echo "==> [8/8] 生成 metadata ..."

META_DIR="$SCRIPT_DIR/gstshark_meta"
rm -rf "$META_DIR"
mkdir -p "$META_DIR"

# 找一个可用的 src 元素
SRC_ELEM=""
for elem in fakesrc audiotestsrc videotestsrc; do
  if gst-inspect-1.0 "$elem" >/dev/null 2>&1; then
    SRC_ELEM="$elem"
    break
  fi
done

if [ -z "$SRC_ELEM" ]; then
  echo "ERROR: 找不到可用的 src 元素 (fakesrc/audiotestsrc/videotestsrc 都不可用)"
  exit 1
fi

echo "    使用 src 元素: $SRC_ELEM"
echo "    tracer 列表: $TRACERS"

GST_SHARK_LOCATION="$META_DIR/" \
GST_DEBUG="GST_TRACER:7" \
GST_TRACERS="$TRACERS" \
gst-launch-1.0 -q "$SRC_ELEM" num-buffers=1 ! fakesink 2>/dev/null

if [ ! -s "$META_DIR/metadata" ]; then
  echo "ERROR: metadata 生成失败 (文件不存在或为空)"
  echo "       尝试查看完整日志:"
  echo "         GST_SHARK_LOCATION=$META_DIR/ GST_DEBUG='GST_TRACER:9' GST_TRACERS='$TRACERS' \\"
  echo "         gst-launch-1.0 -q $SRC_ELEM num-buffers=1 ! fakesink"
  exit 1
fi

echo "    metadata 已生成: $META_DIR/metadata"
ls -lh "$META_DIR/" | sed 's/^/      /'


# ============================================================================
# 8. 与 Android datastream 配对 (可选)
# ============================================================================
TRACING_DIR="$SCRIPT_DIR/tracing_data"

if [ -n "$ANDROID_DATASTREAM" ]; then
  echo
  echo "==> 与 Android datastream 配对 ..."

  if [ ! -f "$ANDROID_DATASTREAM" ]; then
    echo "ERROR: 找不到 Android datastream: $ANDROID_DATASTREAM"
    exit 1
  fi

  rm -rf "$TRACING_DIR"
  mkdir -p "$TRACING_DIR"

  cp "$META_DIR/metadata" "$TRACING_DIR/metadata"
  cp "$ANDROID_DATASTREAM" "$TRACING_DIR/datastream"

  # 提取 Android datastream 里的真实 UUID
  REAL_UUID=$(xxd -p -l 16 -s 4 "$TRACING_DIR/datastream" 2>/dev/null | tr -d '\n' | \
    awk '{printf "%s-%s-%s-%s-%s", substr($0,1,8), substr($0,9,4), substr($0,13,4), substr($0,17,4), substr($0,21,12)}')

  if [ -n "$REAL_UUID" ] && [ ${#REAL_UUID} -eq 36 ]; then
    echo "    Android UUID: $REAL_UUID"
    sed -i "s/uuid = \"[^\"]*\"/uuid = \"$REAL_UUID\"/" "$TRACING_DIR/metadata"
    echo "    已更新 metadata 的 UUID"
  else
    echo "    WARN: 无法从 datastream 提取 UUID, 保持 metadata 原值"
  fi

  # 验证
  echo "    用 babeltrace 验证:"
  if babeltrace "$TRACING_DIR/" 2>/dev/null | head -5 | sed 's/^/      /'; then
    echo "    验证成功"
  else
    echo "    WARN: babeltrace 验证失败, 手动检查"
    echo "          babeltrace $TRACING_DIR/"
  fi

  echo
  echo "    配对目录: $TRACING_DIR"
  ls -lh "$TRACING_DIR/" | sed 's/^/      /'
fi


# ============================================================================
# 9. 可选: 画图
# ============================================================================
if [ "$RUN_PLOT" = "1" ] && [ -d "$TRACING_DIR" ]; then
  echo
  echo "==> 运行 gstshark-plot ..."

  if ! command -v octave >/dev/null 2>&1; then
    echo "ERROR: 未安装 octave, 无法运行 gstshark-plot"
    echo "       sudo apt install -y octave epstool"
    exit 1
  fi

  cd "$SCRIPT_DIR/scripts/graphics"
  ./gstshark-plot "$TRACING_DIR/" -s png

  echo "    图表已生成在: $TRACING_DIR/"
  ls -lh "$TRACING_DIR"/*.png 2>/dev/null | sed 's/^/      /'
fi


# ============================================================================
# 完成
# ============================================================================
echo
echo "============================================================"
echo " 全部完成"
echo "============================================================"
echo " 插件安装 : $LIBDIR/gstreamer-$GST_API_VERSION/libgstsharktracers.so"
echo " metadata : $META_DIR/metadata"
if [ -n "$ANDROID_DATASTREAM" ]; then
  echo " 配对目录 : $TRACING_DIR"
fi
echo
echo " 以后复用 metadata:"
echo "   cp $META_DIR/metadata /path/to/tracing_data/"
echo "   cp /path/to/android/datastream /path/to/tracing_data/"
echo "   # UUID 对齐"
echo "   cd /path/to/tracing_data"
echo "   REAL_UUID=\$(xxd -p -l 16 -s 4 datastream | tr -d '\\n' | \\"
echo "     awk '{printf \"%s-%s-%s-%s-%s\", substr(\$0,1,8), substr(\$0,9,4), substr(\$0,13,4), substr(\$0,17,4), substr(\$0,21,12)}')"
echo "   sed -i \"s/uuid = \\\"[^\\\"]*\\\"/uuid = \\\"\$REAL_UUID\\\"/\" metadata"
echo "   # 画图"
echo "   cd $SCRIPT_DIR/scripts/graphics"
echo "   ./gstshark-plot /path/to/tracing_data/ -s png"
echo "============================================================"

(c)运行gst-launch-1.0插件抓取数据

bash 复制代码
# 在 Ubuntu 上跑一个 10 秒的视频测试
GST_SHARK_LOCATION=/home/root/gst-shark/tracing_data/ \
GST_DEBUG="GST_TRACER:7" \
GST_TRACERS="proctime;interlatency;framerate;cpuusage;bitrate;queuelevel" \
gst-launch-1.0 -q videotestsrc num-buffers=300 ! x264enc ! fakesink

gst-shark/tracing_data下生成datastream和metadata(只需要metadata)

gst-shark数据分析

数据分析有几种方式:gstshark-plot/babeltrace/Trace Compass

Trace Compass下载地址: https://eclipse.dev/tracecompass/download.html

(1)将datastream和metadata置于同一个目录下

(2)open trace file打开datastream文件

metadata:

复制代码
/* CTF 1.8 */
typealias integer { size = 8; align = 8; signed = false; } := uint8_t;
typealias integer { size = 16; align = 8; signed = false; } := uint16_t;
typealias integer { size = 32; align = 8; signed = false; } := uint32_t;
typealias integer { size = 64; align = 8; signed = false; } := uint64_t;

trace {
    major = 1;
    minor = 3;
    uuid = "d18e6374-35a1-cd42-8e70-a9cffa712793";
    byte_order = le;
    packet.header := struct {
        uint32_t magic;
        uint8_t  uuid[16];
        uint32_t stream_id;
    };
};

clock { 
    name = monotonic; 
    uuid = "84db105b-b3f4-4821-b662-efc51455106a"; 
    description = "Monotonic Clock"; 
    freq = 1000000; /* Frequency, in Hz */ 
    /* clock value offset from Epoch is: offset * (1/freq) */ 
    offset_s = 21600; 
};

typealias integer {
    size = 32; align = 8; signed = false;
    map = clock.monotonic.value;
} := uint32_clock_monotonic_t;

typealias integer {
    size = 64; align = 8; signed = false;
    map = clock.monotonic.value;
} := uint64_clock_monotonic_t;

struct packet_context {
    uint64_clock_monotonic_t timestamp_begin;
    uint64_clock_monotonic_t timestamp_end;
};

struct event_header {
    enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;
    variant <id> {
        struct {
            uint32_t timestamp;
        } compact;
        struct {
            uint32_t id;
            uint64_t timestamp;
        } extended;
    } v;
} align(8);

stream {
    id = 0;
    event.header := struct event_header;
    packet.context := struct packet_context;
};

event {
    name = init;
    id = 0;
    stream_id = 0;
};
event {
    name = proctime;
    id = 2;
    stream_id = 0;
    fields := struct {
        string element; 
        integer { size = 64; align = 8; signed = 0; encoding = none; base = 10; } _time;
    };
};

event {
    name = interlatency;
    id = 3;
    stream_id = 0;
    fields := struct {
        string from_pad;
        string to_pad;
        integer { size = 64; align = 8; signed = 0; encoding = none; base = 10; } _time;
    };
};

event {
    name = queuelevel;
    id = 6;
    stream_id = 0;
    fields := struct {
        string queue;
        integer { size = 32; align = 8; signed = 0; encoding = none; base = 10; } size_bytes;
        integer { size = 32; align = 8; signed = 0; encoding = none; base = 10; } max_size_bytes;
         integer { size = 32; align = 8; signed = 0; encoding = none; base = 10; } size_buffers;
        integer { size = 32; align = 8; signed = 0; encoding = none; base = 10; } max_size_buffers;
        integer { size = 64; align = 8; signed = 0; encoding = none; base = 10; } size_time;
        integer { size = 64; align = 8; signed = 0; encoding = none; base = 10; } max_size_time;
    };
};

event {
    name = bitrate;
    id = 7;
    stream_id = 0;
    fields := struct {
        string pad;
        integer { size = 64; align = 8; signed = 0; encoding = none; base = 10; } _bps;
    };
};

event {
    name = cpuusage;
    id = 1;
    stream_id = 0;
    fields := struct {
        floating_point { exp_dig = 8; mant_dig = 24; byte_order = le; align = 8; } _cpu0;
        floating_point { exp_dig = 8; mant_dig = 24; byte_order = le; align = 8; } _cpu1;
        floating_point { exp_dig = 8; mant_dig = 24; byte_order = le; align = 8; } _cpu2;
        floating_point { exp_dig = 8; mant_dig = 24; byte_order = le; align = 8; } _cpu3;
        floating_point { exp_dig = 8; mant_dig = 24; byte_order = le; align = 8; } _cpu4;
        floating_point { exp_dig = 8; mant_dig = 24; byte_order = le; align = 8; } _cpu5;
        floating_point { exp_dig = 8; mant_dig = 24; byte_order = le; align = 8; } _cpu6;
        floating_point { exp_dig = 8; mant_dig = 24; byte_order = le; align = 8; } _cpu7;
        floating_point { exp_dig = 8; mant_dig = 24; byte_order = le; align = 8; } _cpu8;
        floating_point { exp_dig = 8; mant_dig = 24; byte_order = le; align = 8; } _cpu9;
        floating_point { exp_dig = 8; mant_dig = 24; byte_order = le; align = 8; } _cpu10;
        floating_point { exp_dig = 8; mant_dig = 24; byte_order = le; align = 8; } _cpu11;
        floating_point { exp_dig = 8; mant_dig = 24; byte_order = le; align = 8; } _cpu12;
        floating_point { exp_dig = 8; mant_dig = 24; byte_order = le; align = 8; } _cpu13;
        floating_point { exp_dig = 8; mant_dig = 24; byte_order = le; align = 8; } _cpu14;
        floating_point { exp_dig = 8; mant_dig = 24; byte_order = le; align = 8; } _cpu15;
    };
};

event {
    name = framerate;
    id = 4;
    stream_id = 0;
    fields := struct {
        string pad;
        integer { size = 64; align = 8; signed = 0; encoding = none; base = 10; } _fps;
    };
};
相关推荐
2501_915921435 小时前
iOS应用耗电测试:使用Energy Log、Keymob和Instruments进行性能监控与能源消耗分析
android·ios·小程序·https·uni-app·iphone·webview
蒲公英内测分发5 小时前
热成像仪交付客户后,Android 配套 App 不上架,怎么持续下载安装和更新
android·机器人·项目管理·智能家居·智能硬件
脚踏实地,坚持不懈!6 小时前
Android 常见性能问题总结
android
高旭的旭6 小时前
图解 Android 系统架构:从 App、Framework、HAL 到 Linux 驱动
android·linux·系统架构
hunterandroid7 小时前
Navigation Component 多模块导航与深度链接实战
android
快乐非自愿7 小时前
低代码落地费控系统:从报销流程到业财闭环的实操方案
android·低代码·rxjava·ai低代码
摇滚侠7 小时前
《Java并发编程之美》第 2 章 并发编程的其他基础知识 阅读笔记 3
android·java·笔记
YB13758 小时前
Jetpack Compose 动画updateTransition
android
用户2018792831678 小时前
给主厨装一台打卡机:一文搞懂 Android APM 性能监控原理
android