Perfetto使用教程

Perfetto 是谷歌开发的用于性能检测和跟踪分析的生产级开源堆栈。它提供用于记录系统级和应用程序级跟踪的服务和库,包括本机 + Java堆分析,使用SQL分析跟踪数据的C++库以及基于Web的可视化UI,能够清晰地展示GB级别体量的跟踪数据。Perfetto 适用于Linux内核和Android的整个系统范围内的性能数据收集、可视化及数据分析。


开启设备追踪

Android 11 (R)及以上默认开启Perfetto,Android 9 (P)和10 (Q) 需要手动开启,其他设备暂不支持:

# Needed only on Android 9 (P) and 10 (Q) on non-Pixel phones.
adb shell setprop persist.traced.enable 1

启动抓取方式

通过脚本工具tools/record_android_trace,使用命令行工具启动设备抓取

 adb shell perfetto [ --time TIMESPEC ] [ --buffer SIZE ] [ --size SIZE ]
    [ ATRACE_CAT | FTRACE_GROUP/FTRACE_NAME]...

通过抓取页面Perfetto UI

Perfetto UI抓取简介

Recording settings

三种录制设置,适用不同的使用场景

Stop when full

  • in-memory buffer size
    设置存储trace的最大buffer大小,存储在内存中,不会影响到IO。
  • Max duration
    设置此次 trace 抓取的最大时长
    该模式下Perfetto的停止受buffer大小和trace时长的控制,满足两个任意条件之一,即可停止trace。
    优点:trace不会因为overwrite而导致丢失。
    缺点:如果trace太多,会导致提前结束,无法录制到出现问题时候的trace。


Ring buffer

该模式只会收到Max duration的影响,时间到了就停止抓取trace,但是trace会有被overwrite的风险。


Long trace

用于长时间地抓取trace,但是由于需要定时将buffer中的trace写到文件里面去,会有IO的影响。
Flush on disk every
间隔多长时间将buffer中的trace写入到文件,这个数值不能太大也不能太小,太大容易丢trace,太小容易出现IO问题。trace的停止主要受控于Max duration,或者手动停止。


CPU

CPU相关的常见功能统计


  • Coarse CPU usage counter
    粗粒度的CPU相关使用统计
  • Scheduling details
    详细查看CPU运行的每个task


  • CPU frequency and idle states
    查看每颗CPU的运行频率和是否空闲


  • Syscalls
    记录每一个系统调用,对性能影响较大,需要userdebug版本。

GPU

记录GPU的主频和内存使用


  • GPU frequency
    记录GPU的频率


  • GPU memory
    GPU内存使用量及各个进程的占用,仅支持Android 12+


Power

Memory

Android apps & svcs

  • Atrace userspace annotations
    开启这个选项之后,选择合适的atrace tag就可以开启对应的trace了,这个tag就对应了System Tracing的界面的catergray。
  • Event log (logcat)
    可以实时记录log,然后将log和trace信息一一对应。 选择合适的log类型,就可以记录相应的log。


Chrome

如果需要分析webview相关的性能问题,可以开启该选项的相关功能


Stack Samples

  • Callstack sampling
    定期记录进程的当前函数调用堆栈

Advanced settings

目前只有一个开启ftrace功能,用于分析内核性能问题,可以选择相应的tag进行记录


Recording command

配置汇总和执行,完成上述参数配置后,可以在改选项中获得所有的配置参数,拷贝命令,直接adb开启录制追踪。


adb shell perfetto \
  -c - --txt \
  -o /data/misc/perfetto-traces/trace \
<<EOF

buffers: {
    size_kb: 63488
    fill_policy: DISCARD
}
buffers: {
    size_kb: 2048
    fill_policy: DISCARD
}
data_sources: {
    config {
        name: "android.packages_list"
        target_buffer: 1
    }
}
data_sources: {
    config {
        name: "android.log"
        android_log_config {
            log_ids: LID_KERNEL
            log_ids: LID_DEFAULT
            log_ids: LID_RADIO
            log_ids: LID_SECURITY
            log_ids: LID_STATS
            log_ids: LID_SYSTEM
        }
    }
}
data_sources: {
    config {
        name: "linux.perf"
        perf_event_config {
            timebase {
                frequency: 100
                timestamp_clock: PERF_CLOCK_BOOTTIME
            }
            callstack_sampling {
            }
        }
    }
}
data_sources: {
    config {
        name: "org.chromium.trace_event"
        chrome_config {
            trace_config: "{\"record_mode\":\"record-until-full\",\"included_categories\":[\"accessibility\",\"audio\",\"aogh\",\"android_webview.timeline\",\"android_webview\",\"disabled-by-default-audio-worklet\",\"disabled-by-default-animation-worklet\",\"disabled-by-default-blink.debug\",\"log\",\"toplevel\",\"toplevel.flow\",\"scheduler\",\"sequence_manager\",\"disabled-by-default-toplevel.flow\",\"disabled-by-default-ipc.flow\",\"mojom\",\"v8\",\"blink\",\"cc\",\"gpu\",\"viz\",\"ui\",\"views\",\"benchmark\",\"evdev\",\"input\",\"loading\",\"net\",\"netlog\",\"navigation\",\"browser\"],\"excluded_categories\":[\"*\"],\"memory_dump_config\":{}}"
            privacy_filtering_enabled: true
            client_priority: USER_INITIATED
        }
    }
}
data_sources: {
    config {
        name: "track_event"
        chrome_config {
            trace_config: "{\"record_mode\":\"record-until-full\",\"included_categories\":[\"accessibility\",\"audio\",\"aogh\",\"android_webview.timeline\",\"android_webview\",\"disabled-by-default-audio-worklet\",\"disabled-by-default-animation-worklet\",\"disabled-by-default-blink.debug\",\"log\",\"toplevel\",\"toplevel.flow\",\"scheduler\",\"sequence_manager\",\"disabled-by-default-toplevel.flow\",\"disabled-by-default-ipc.flow\",\"mojom\",\"v8\",\"blink\",\"cc\",\"gpu\",\"viz\",\"ui\",\"views\",\"benchmark\",\"evdev\",\"input\",\"loading\",\"net\",\"netlog\",\"navigation\",\"browser\"],\"excluded_categories\":[\"*\"],\"memory_dump_config\":{}}"
            privacy_filtering_enabled: true
            client_priority: USER_INITIATED
        }
        track_event_config {
            disabled_categories: "*"
            enabled_categories: "accessibility"
            enabled_categories: "audio"
            enabled_categories: "aogh"
            enabled_categories: "android_webview.timeline"
            enabled_categories: "android_webview"
            enabled_categories: "disabled-by-default-audio-worklet"
            enabled_categories: "disabled-by-default-animation-worklet"
            enabled_categories: "disabled-by-default-blink.debug"
            enabled_categories: "log"
            enabled_categories: "toplevel"
            enabled_categories: "toplevel.flow"
            enabled_categories: "scheduler"
            enabled_categories: "sequence_manager"
            enabled_categories: "disabled-by-default-toplevel.flow"
            enabled_categories: "disabled-by-default-ipc.flow"
            enabled_categories: "mojom"
            enabled_categories: "v8"
            enabled_categories: "blink"
            enabled_categories: "cc"
            enabled_categories: "gpu"
            enabled_categories: "viz"
            enabled_categories: "ui"
            enabled_categories: "views"
            enabled_categories: "benchmark"
            enabled_categories: "evdev"
            enabled_categories: "input"
            enabled_categories: "loading"
            enabled_categories: "net"
            enabled_categories: "netlog"
            enabled_categories: "navigation"
            enabled_categories: "browser"
            enabled_categories: "__metadata"
            timestamp_unit_multiplier: 1000
            filter_debug_annotations: true
            enable_thread_time_sampling: true
            filter_dynamic_event_names: true
        }
    }
}
data_sources: {
    config {
        name: "org.chromium.trace_metadata"
        chrome_config {
            trace_config: "{\"record_mode\":\"record-until-full\",\"included_categories\":[\"accessibility\",\"audio\",\"aogh\",\"android_webview.timeline\",\"android_webview\",\"disabled-by-default-audio-worklet\",\"disabled-by-default-animation-worklet\",\"disabled-by-default-blink.debug\",\"log\",\"toplevel\",\"toplevel.flow\",\"scheduler\",\"sequence_manager\",\"disabled-by-default-toplevel.flow\",\"disabled-by-default-ipc.flow\",\"mojom\",\"v8\",\"blink\",\"cc\",\"gpu\",\"viz\",\"ui\",\"views\",\"benchmark\",\"evdev\",\"input\",\"loading\",\"net\",\"netlog\",\"navigation\",\"browser\"],\"excluded_categories\":[\"*\"],\"memory_dump_config\":{}}"
            privacy_filtering_enabled: true
            client_priority: USER_INITIATED
        }
    }
}
data_sources: {
    config {
        name: "android.heapprofd"
        target_buffer: 0
        heapprofd_config {
            sampling_interval_bytes: 4096
            continuous_dump_config {
                dump_phase_ms: 30000
                dump_interval_ms: 10000
            }
            shmem_size_bytes: 8388608
            block_client: true
        }
    }
}
data_sources: {
    config {
        name: "linux.ftrace"
        ftrace_config {
            ftrace_events: "clk/*"
            ftrace_events: "ext4/*"
            ftrace_events: "f2fs/*"
            ftrace_events: "fastrpc/*"
            ftrace_events: "i2c/*"
            ftrace_events: "irq/*"
            ftrace_events: "kmem/*"
            ftrace_events: "memory_bus/*"
            ftrace_events: "mmc/*"
            ftrace_events: "oom/*"
            ftrace_events: "power/*"
            ftrace_events: "regulator/*"
            ftrace_events: "sched/*"
            ftrace_events: "sync/*"
            ftrace_events: "task/*"
            ftrace_events: "ftrace/print"
            atrace_categories: "am"
            atrace_categories: "adb"
            atrace_categories: "aidl"
            atrace_categories: "dalvik"
            atrace_categories: "audio"
            atrace_categories: "binder_lock"
            atrace_categories: "binder_driver"
            atrace_categories: "bionic"
            atrace_categories: "camera"
            atrace_categories: "database"
            atrace_categories: "gfx"
            atrace_categories: "hal"
            atrace_categories: "input"
            atrace_categories: "network"
            atrace_categories: "nnapi"
            atrace_categories: "pm"
            atrace_categories: "power"
            atrace_categories: "rs"
            atrace_categories: "res"
            atrace_categories: "rro"
            atrace_categories: "sm"
            atrace_categories: "ss"
            atrace_categories: "vibrator"
            atrace_categories: "video"
            atrace_categories: "view"
            atrace_categories: "webview"
            atrace_apps: "*"
            buffer_size_kb: 512
            drain_period_ms: 100
        }
    }
}
duration_ms: 40000

EOF

trace 文件获取

使用 adb pull /data/misc/perfetto-traces/trace ~/trace.perfetto-trace 提取跟踪日志文件并在 Perfetto UI 中打开它。

trace文件过大加载问题

当trace文件大于1G时,Open trace file会出现oops 内存溢出无法访问。



可以使用Trace Processor来加载trace文件

# Download prebuilts (Linux and Mac only)
curl -LO https://get.perfetto.dev/trace_processor
chmod +x ./trace_processor

# Start the interactive shell
./trace_processor trace.perfetto-trace

# Start a local trace processor instance to replace wasm module in the UI
./trace_processor trace.perfetto-trace --httpd

执行./trace_processor trace.perfetto-trace --httpd后,Chrome浏览器打开 https://ui.perfetto.dev/#!/ ,会自动检测本地是否已经有trace_processor生成的HTTP SERVER(9001端口),如下图提示,请选择“YES, use loaded trace”,将自动解析 trace_processor已经加载的pftrace文件。

分析方法简介

slice 片段

对应代码中 Trace.beginSection/ATRACE_BEGIN 记录的事件,选中后会显示黑色边框


counter

计数器,记录离散的数值点,对应代码中 Trace.beginCounter/ATRACE_INT 记录的事件


CPU调度和频率

线程状态

点击片段上方线程调度信息片段(Running),可以看到线程当前运行在哪个CPU上



点击箭头 ,可以在CPU调度中看到该运行片段以及调度时延信息。



被P: /system/bin/traced_probes [1488]T: traced_probes [1488]线程唤醒,从就绪到运行延迟了 774us 792ns,再次点击箭头,可以回到原片段,这个跳转比较灵活方便。

锁竞争(lock contention)

在lock contention 片段上,可以点击上边的monitor contention来查看当前对象锁竞争发生的调用栈,如下详情中显示当前对象锁被Owner (Binder:1754_16)持有,其持锁当前运行在serviceDoneExecuting (AMS.java 16426行),且当前等待该对象锁的线程已经有5个了;当前线程执行被阻塞在getUidState方法中(AMS.java 6614行)。


SQL查询

Perfetto支持通过sql语句查询数据,提供常见的格式和关键字段信息:

  • slice表,横向track上的一条条小片段

  • ts:片段起始时间戳(单位ns)

  • dur:片段持续时长(ns)

  • track_id属于哪个track(水平timeline)

  • name: 片段标注的名称,对应Trace中打印的方法名、标记等信息

  • thread_track表,utid标识线程tid,并不是真实的线程tid

  • thread表,表示各线程信息,其中utid和thread_track表的utid关联

  • process表,upid和thread表的upid关联,表示线程所属的父进程

  • sched_slice,线程调度片段

  • thread_state,各track上边的线程调度片段,标识线程运行状态

Slices

横向轴上的一小段时间片段,具有指定的名称


> SELECT ts, dur, name FROM slice
ts                   dur                  name
-------------------- -------------------- ---------------------------
     261187017446933               358594 eglSwapBuffersWithDamageKHR
     261187017518340                  357 onMessageReceived
     261187020825163                 9948 queueBuffer
     261187021345235                  642 bufferLoad
     261187121345235                  153 query
     ...

Counters

根据时间记录的离散数值点



···

SELECT ts, value FROM counter
ts value


 261187012149954          1454.000000
 261187012399172          4232.000000
 261187012447402         14304.000000
 261187012535839         15490.000000
 261187012590890         17490.000000
 261187012590890         16590.000000

...
···

Scheduler slices

CPU运行的详细task


> SELECT ts, dur, cpu, utid FROM sched
ts                   dur                  cpu                  utid
-------------------- -------------------- -------------------- --------------------
     261187012170489               267188                    0                  390
     261187012170995               247153                    1                  767
     261187012418183                12812                    2                 2790
     261187012421099               220000                    6                  683
     261187012430995                72396                    7                 2791
...

参考文章

1、 https://perfetto.dev/docs/

2、https://ui.perfetto.dev/#!/record

3、https://www.cnblogs.com/treecarrybear/p/17056783.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351

推荐阅读更多精彩内容