systrace là công cụ chính để phân tích hiệu suất của thiết bị Android. Tuy nhiên, đây thực sự là một trình bao bọc xung quanh các công cụ khác. Đây là trình bao bọc phía máy chủ xung quanh atrace, tệp thực thi phía thiết bị kiểm soát tính năng theo dõi không gian người dùng và thiết lập ftrace cũng như cơ chế theo dõi chính trong nhân Linux. systrace sử dụng atrace để bật tính năng theo dõi, sau đó đọc vùng đệm ftrace và gói tất cả vào một trình xem HTML độc lập. (Mặc dù các hạt nhân mới hơn có hỗ trợ Bộ lọc gói Berkeley nâng cao của Linux (eBPF), nhưng tài liệu sau đây liên quan đến hạt nhân 3.18 (không có eFPF) vì đó là hạt nhân được sử dụng trên Pixel/Pixel XL.)
systrace thuộc sở hữu của các nhóm Google Android và Google Chrome, đồng thời là nguồn mở trong dự án Catapult. Ngoài systrace, Catapult còn bao gồm các tiện ích hữu ích khác. Ví dụ: ftrace có nhiều tính năng hơn so với những tính năng mà systrace hoặc atrace có thể trực tiếp bật và chứa một số chức năng nâng cao rất quan trọng để gỡ lỗi các vấn đề về hiệu suất. (Những tính năng này yêu cầu quyền truy cập thư mục gốc và thường là một hạt nhân mới.)
Chạy systrace
Khi gỡ lỗi hiện tượng giật trên Pixel/Pixel XL, hãy bắt đầu bằng lệnh sau:
./systrace.py sched freq idle am wm gfx view sync binder_driver irq workq input -b 96000
Khi kết hợp với các điểm theo dõi bổ sung cần thiết cho GPU và hoạt động của quy trình hiển thị, bạn có thể theo dõi từ hoạt động đầu vào của người dùng đến khung hiển thị trên màn hình. Đặt kích thước vùng đệm thành một kích thước lớn để tránh mất các sự kiện (vì nếu không có vùng đệm lớn, một số CPU sẽ không chứa sự kiện nào sau một số điểm trong dấu vết).
Khi xem xét systrace, hãy lưu ý rằng mọi sự kiện đều được kích hoạt bởi một sự kiện nào đó trên CPU.
Vì systrace được xây dựng dựa trên ftrace và ftrace chạy trên CPU, nên một phần nào đó trên CPU phải ghi vùng đệm ftrace để ghi lại các thay đổi về phần cứng. Điều này có nghĩa là nếu tò mò về lý do hàng rào hiển thị thay đổi trạng thái, bạn có thể xem nội dung đang chạy trên CPU tại thời điểm chuyển đổi chính xác (một nội dung nào đó đang chạy trên CPU đã kích hoạt thay đổi đó trong nhật ký). Khái niệm này là nền tảng để phân tích hiệu suất bằng systrace.
Ví dụ: Khung đang hoạt động
Ví dụ này mô tả một systrace cho quy trình giao diện người dùng thông thường. Để làm theo ví dụ này, hãy tải tệp zip của các dấu vết xuống (cũng bao gồm các dấu vết khác được đề cập trong phần này), giải nén tệp và mở tệp systrace_tutorial.html
trong trình duyệt. Xin lưu ý rằng systrace này là một tệp lớn; trừ phi bạn sử dụng systrace trong công việc hằng ngày, đây có thể là một dấu vết lớn hơn nhiều với nhiều thông tin hơn bạn từng thấy trong một dấu vết duy nhất trước đây.
Đối với khối lượng công việc nhất quán, định kỳ như TouchLatency, quy trình giao diện người dùng chứa những nội dung sau:
- EventThread trong SurfaceFlinger đánh thức luồng giao diện người dùng của ứng dụng, báo hiệu rằng đã đến lúc kết xuất một khung hình mới.
- Ứng dụng kết xuất một khung trong luồng giao diện người dùng, RenderThread và hwuiTasks, sử dụng tài nguyên CPU và GPU. Đây là phần lớn dung lượng dành cho giao diện người dùng.
- Ứng dụng gửi khung hình đã kết xuất đến SurfaceFlinger bằng một liên kết, sau đó SurfaceFlinger chuyển sang trạng thái ngủ.
- EventThread thứ hai trong SurfaceFlinger sẽ đánh thức SurfaceFlinger để kích hoạt thành phần hiển thị và đầu ra. Nếu SurfaceFlinger xác định rằng không có công việc nào cần làm, thì lớp này sẽ chuyển sang trạng thái ngủ.
- SurfaceFlinger xử lý thành phần bằng cách sử dụng Trình kết hợp phần cứng (HWC)/Trình kết hợp phần cứng 2 (HWC2) hoặc GL. Cấu trúc HWC/HWC2 nhanh hơn và tiêu thụ ít năng lượng hơn nhưng có các giới hạn tuỳ thuộc vào hệ thống trên một khối xếp (SoC). Quá trình này thường mất khoảng 4 đến 6 mili giây, nhưng có thể trùng lặp với bước 2 vì các ứng dụng Android luôn được lưu vào bộ đệm ba lần. (Mặc dù các ứng dụng luôn được lưu vào bộ đệm ba lần, nhưng có thể chỉ có một khung đang chờ trong SurfaceFlinger, khiến ứng dụng đó có vẻ giống với bộ đệm đôi.)
- SurfaceFlinger điều phối đầu ra cuối cùng để hiển thị bằng trình điều khiển của nhà cung cấp và quay lại chế độ ngủ, chờ EventThread đánh thức.
Hãy cùng xem xét khung hình bắt đầu từ 15409 ms:

Hình 1 là một khung hình thông thường được bao quanh bởi các khung hình thông thường, vì vậy, đây là một điểm xuất phát tốt để hiểu cách hoạt động của quy trình giao diện người dùng. Hàng luồng giao diện người dùng cho TouchLatency bao gồm nhiều màu sắc ở các thời điểm khác nhau. Các thanh biểu thị các trạng thái khác nhau của luồng:
- Xám. Ngủ.
- Màu xanh dương. Có thể chạy (có thể chạy nhưng trình lập lịch biểu chưa chọn để chạy).
- Xanh lục. Đang chạy (trình lập lịch biểu cho rằng tác vụ này đang chạy).
- Đỏ. Chế độ ngủ không thể ngắt (thường ngủ trên một khoá trong nhân). Có thể cho biết mức tải I/O. Rất hữu ích khi gỡ lỗi các vấn đề về hiệu suất.
- Cam. Chế độ ngủ không thể gián đoạn do tải I/O.
Để xem lý do ngủ không thể gián đoạn (có trong điểm theo dõi sched_blocked_reason
), hãy chọn lát cắt ngủ không thể gián đoạn màu đỏ.
Khi EventThread đang chạy, luồng giao diện người dùng cho TouchLatency sẽ có thể chạy. Để xem điều gì đã đánh thức thiết bị, hãy nhấp vào phần màu xanh dương.

Hình 2 cho thấy luồng giao diện người dùng TouchLatency được đánh thức bằng tid 6843, tương ứng với EventThread. Luồng giao diện người dùng sẽ đánh thức, kết xuất một khung hình và đưa khung hình đó vào hàng đợi để SurfaceFlinger sử dụng.

Nếu thẻ binder_driver
được bật trong một dấu vết, bạn có thể chọn một giao dịch liên kết để xem thông tin về tất cả các quy trình liên quan đến giao dịch đó.

Hình 4 cho thấy rằng, tại 15.423,65 ms, Binder:6832_1 trong SurfaceFlinger trở thành có thể chạy được do tid 9579, là RenderThread của TouchLatency. Bạn cũng có thể thấy queueBuffer ở cả hai bên của giao dịch liên kết.
Trong queueBuffer ở phía SurfaceFlinger, số lượng khung đang chờ xử lý từ TouchLatency sẽ tăng từ 1 lên 2.

Hình 5 cho thấy tính năng đệm ba lần, trong đó có hai khung hình đã hoàn tất và ứng dụng sắp bắt đầu kết xuất khung hình thứ ba. Lý do là chúng ta đã bỏ qua một số khung hình, vì vậy, ứng dụng sẽ giữ lại hai khung hình đang chờ xử lý thay vì một khung hình để cố gắng tránh bỏ qua thêm khung hình.
Ngay sau đó, luồng chính của SurfaceFlinger được EventThread thứ hai đánh thức để có thể xuất khung hình cũ đang chờ xử lý lên màn hình:

Trước tiên, SurfaceFlinger sẽ khoá bộ đệm đang chờ xử lý cũ, khiến số lượng bộ đệm đang chờ xử lý giảm từ 2 xuống 1.

Sau khi khoá vùng đệm, SurfaceFlinger sẽ thiết lập thành phần và gửi khung hình cuối cùng đến màn hình. (Một số phần trong số này được bật trong điểm theo dõi mdss
, vì vậy, chúng có thể không có trong SoC của bạn.)

Tiếp theo, mdss_fb0
sẽ đánh thức CPU 0. mdss_fb0
là luồng hạt nhân của quy trình hiển thị để xuất khung đã kết xuất sang màn hình.
Chúng ta có thể thấy mdss_fb0
dưới dạng một hàng riêng trong dấu vết (cuộn xuống để xem).

mdss_fb0
thức dậy, chạy trong thời gian ngắn, chuyển sang trạng thái ngủ không thể gián đoạn, sau đó thức dậy lần nữa.
Ví dụ: Khung không hoạt động
Ví dụ này mô tả một systrace dùng để gỡ lỗi hiện tượng giật trên Pixel/Pixel XL. Để làm theo ví dụ, hãy tải tệp zip xuống của các dấu vết (bao gồm cả các dấu vết khác được đề cập trong phần này), giải nén tệp và mở tệp systrace_tutorial.html
trong trình duyệt.
Khi mở systrace, bạn sẽ thấy nội dung như sau:

Khi tìm kiếm hiện tượng giật, hãy kiểm tra hàng FrameMissed trong SurfaceFlinger.
FrameMissed là một tính năng cải thiện chất lượng cuộc sống do HWC2 cung cấp. Khi xem
systrace cho các thiết bị khác, hàng FrameMissed có thể không xuất hiện nếu thiết bị
không sử dụng HWC2. Trong cả hai trường hợp, FrameMissed có liên quan đến việc SurfaceFlinger thiếu một trong các thời gian chạy cực kỳ thường xuyên và số lượng bộ đệm đang chờ xử lý không thay đổi cho ứng dụng (com.prefabulated.touchlatency
) ở vsync.

Hình 11 cho thấy một khung hình bị bỏ lỡ ở 15598,29&nbps;ms. SurfaceFlinger đã thức dậy trong giây lát ở khoảng thời gian vsync và quay lại trạng thái ngủ mà không làm gì cả, tức là SurfaceFlinger đã xác định rằng không đáng để cố gắng gửi lại một khung hình đến màn hình. Tại sao?
Để hiểu cách quy trình bị hỏng cho khung này, trước tiên, hãy xem lại ví dụ về khung đang hoạt động ở trên để xem quy trình giao diện người dùng thông thường xuất hiện như thế nào trong systrace. Khi đã sẵn sàng, hãy quay lại khung hình bị bỏ lỡ và làm ngược lại. Lưu ý rằng SurfaceFlinger sẽ thức dậy và ngay lập tức chuyển sang chế độ ngủ. Khi xem số lượng khung hình đang chờ xử lý từ TouchLatency, có hai khung hình (một gợi ý hay để giúp tìm hiểu xem điều gì đang xảy ra).

Vì chúng ta có các khung trong SurfaceFlinger, nên đây không phải là vấn đề về ứng dụng. Ngoài ra, SurfaceFlinger đang thức dậy đúng thời điểm, vì vậy, đây không phải là vấn đề về SurfaceFlinger. Nếu cả SurfaceFlinger và ứng dụng đều có vẻ bình thường, thì có thể đó là vấn đề về trình điều khiển.
Vì các điểm theo dõi mdss
và sync
được bật, nên chúng ta có thể nhận thông tin về hàng rào (được chia sẻ giữa trình điều khiển màn hình và SurfaceFlinger) kiểm soát thời điểm gửi khung hình đến màn hình.
Các hàng rào này được liệt kê trong mdss_fb0_retire
, cho biết thời điểm một khung hình xuất hiện trên màn hình. Các hàng rào này được cung cấp trong danh mục theo dõi sync
. Hàng rào nào tương ứng với các sự kiện cụ thể trong SurfaceFlinger phụ thuộc vào SOC và ngăn xếp trình điều khiển của bạn, vì vậy, hãy làm việc với nhà cung cấp SOC để hiểu ý nghĩa của các danh mục hàng rào trong dấu vết của bạn.

Hình 13 cho thấy một khung hình được hiển thị trong 33 ms, chứ không phải 16,7 ms như dự kiến. Ở giữa lát cắt đó, khung hình đó phải được thay thế bằng một khung hình mới nhưng lại không được. Xem khung hình trước và tìm bất kỳ điều gì.

Hình 14 cho thấy 14,482 mili giây cho một khung hình. Phân đoạn hai khung hình bị hỏng là 33,6 mili giây, gần giống với những gì chúng ta mong đợi cho hai khung hình (chúng ta kết xuất ở tốc độ 60 Hz, 16,7 mili giây cho mỗi khung hình). Tuy nhiên, 14,482 ms không hề gần với 16,7 ms, điều này cho thấy có gì đó rất sai với quy trình hiển thị.
Hãy điều tra chính xác vị trí kết thúc của hàng rào đó để xác định những yếu tố kiểm soát hàng rào đó.

Một hàng đợi công việc chứa __vsync_retire_work_handler
đang chạy khi hàng rào thay đổi. Khi xem qua nguồn nhân kernel, bạn có thể thấy rằng đó là một phần của trình điều khiển màn hình. Có vẻ như nó nằm trên đường dẫn quan trọng cho quy trình hiển thị, vì vậy, nó phải chạy nhanh nhất có thể. Bạn có thể chạy trong khoảng 70 us (không phải là độ trễ lên lịch lâu), nhưng đây là một hàng đợi công việc và có thể không được lên lịch chính xác.
Kiểm tra khung hình trước đó để xác định xem khung hình đó có đóng góp hay không; đôi khi độ giật có thể tích luỹ theo thời gian và cuối cùng khiến bạn bỏ lỡ thời hạn.

Bạn không thấy dòng có thể chạy trên kworker vì trình xem sẽ chuyển dòng này sang màu trắng khi được chọn, nhưng số liệu thống kê sẽ cho bạn biết: độ trễ của trình lập lịch biểu là 2,3 ms đối với một phần của đường dẫn quan trọng trong quy trình hiển thị là không tốt.
Trước khi tiếp tục, hãy khắc phục độ trễ đó bằng cách di chuyển phần này của đường dẫn quan trọng của quy trình hiển thị từ một workqueue (chạy dưới dạng luồng CFS SCHED_OTHER
) sang một kthread SCHED_FIFO
chuyên dụng. Hàm này cần có các đảm bảo về thời gian mà hàng đợi công việc không thể (và không có ý định) cung cấp.
Đây có phải là lý do gây ra hiện tượng giật không? Thật khó để kết luận chắc chắn. Ngoài các trường hợp dễ chẩn đoán như tranh chấp khoá hạt nhân khiến các luồng quan trọng đối với màn hình chuyển sang trạng thái ngủ, dấu vết thường không chỉ định vấn đề. Liệu hiện tượng giật này có phải là nguyên nhân gây ra tình trạng bị bỏ khung hình không? Hoàn toàn có thể. Thời gian hàng rào phải là 16,7 mili giây, nhưng thời gian này hoàn toàn không gần với thời gian đó trong các khung hình dẫn đến khung hình bị bỏ qua. Do quy trình hiển thị được ghép nối chặt chẽ, nên có thể độ giật xung quanh thời gian hàng rào đã dẫn đến việc bị rớt khung hình.
Trong ví dụ này, giải pháp liên quan đến việc chuyển đổi __vsync_retire_work_handler
từ một workqueue sang một kthread chuyên dụng. Điều này giúp cải thiện đáng kể độ giật và giảm hiện tượng giật trong thử nghiệm bóng nảy. Các dấu vết tiếp theo cho thấy thời gian hàng rào dao động rất gần với 16,7 ms.