systrace là công cụ chính để phân tích hiệu suất thiết bị Android. Tuy nhiên, nó thực sự là một lớp bao bọc xung quanh các công cụ khác. Đó 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 việc theo dõi không gian người dùng và thiết lập ftrace , và cơ chế theo dõi chính trong nhân Linux. systrace sử dụng atrace để cho phép theo dõi, sau đó đọc bộ đệm ftrace và gói tất cả trong 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), tài liệu sau đây liên quan đến hạt nhân 3.18 (không có eFPF) vì đó là những gì đã được sử dụng trên Pixel / Pixel XL.)
systrace thuộc sở hữu của nhóm Google Android và Google Chrome và là mã nguồn mở như một phần của dự án Catapult . Ngoài systrace, Catapult 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 có thể được kích hoạt trực tiếp bởi systrace hoặc atrace 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. (Các tính năng này yêu cầu quyền truy cập root và thường là một nhân mới.)
Chạy hệ thống
Khi gỡ lỗi jitter 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 được kết hợp với các điểm theo dõi bổ sung cần thiết cho hoạt động của GPU và đường dẫn hiển thị, điều này mang lại cho bạn khả năng theo dõi từ đầ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 bộ đệm thành một cái gì đó lớn để tránh mất các sự kiện (vì nếu không có bộ đệm lớn, một số CPU không chứa sự kiện nào sau một số điểm trong dấu vết).
Khi đi qua hệ thống, hãy nhớ rằng mọi sự kiện đều được kích hoạt bởi một thứ gì đó trên CPU .
Bởi vì systrace được xây dựng trên ftrace và ftrace chạy trên CPU, một cái gì đó trên CPU phải ghi bộ đệm ftrace ghi lại các thay đổi phần cứng. Điều này có nghĩa là nếu bạn tò mò về lý do tại sao hàng rào hiển thị thay đổi trạng thái, bạn có thể xem những gì đang chạy trên CPU tại thời điểm chính xác của quá trình chuyển đổi của nó (thứ gì đó đ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 của việc phân tích hiệu suất bằng cách sử dụng systrace.
Ví dụ: Khung làm việc
Ví dụ này mô tả một hệ thống cho một đường ống giao diện người dùng thông thường. Để làm theo ví dụ này, hãy tải xuống tệp zip của các dấu vết (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 của bạn. Hãy cảnh báo rằng hệ thống này là một tệp lớn; trừ khi bạn sử dụng systrace trong công việc hàng ngày của mình, đâ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 trước đây.
Đối với khối lượng công việc nhất quán, định kỳ, chẳng hạn như TouchLatency, đường dẫn giao diện người dùng chứa những điều sau:
- EventThread trong SurfaceFlinger đánh thức chuỗi giao diện người dùng ứng dụng, báo hiệu rằng đã đến lúc hiển thị khung mới.
- Ứng dụng hiển thị khung trong chuỗi 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 kết xuất đến SurfaceFlinger bằng chất kết dính, sau đó SurfaceFlinger chuyển sang chế độ ngủ.
- EventThread thứ hai trong SurfaceFlinger đánh thức SurfaceFlinger để kích hoạt bố cục và hiển thị đầu ra. Nếu SurfaceFlinger xác định rằng không có việc gì phải làm, nó sẽ chuyển sang chế độ ngủ.
- SurfaceFlinger xử lý thành phần bằng Hardware Composer (HWC) / Hardware Composer 2 (HWC2) hoặc GL. Thành phần HWC / HWC2 nhanh hơn và công suất thấp hơn nhưng có những hạn chế tùy thuộc vào hệ thống trên chip (SoC). Quá trình này thường mất ~ 4-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 đệm ba lần. (Trong khi các ứng dụng luôn được đệm ba lần, có thể chỉ có một khung hình đang chờ xử lý đang chờ trong SurfaceFlinger, điều này làm cho nó có vẻ giống với bộ đệm kép.)
- SurfaceFlinger gửi kết quả cuối cùng để hiển thị với trình điều khiển của nhà cung cấp và quay trở lại chế độ ngủ, chờ EventThread đánh thức.
Hãy xem qua khung bắt đầu ở 15409 ms:

Hình 1 là một khung bình thường được bao quanh bởi các khung bình thường, vì vậy đó là một điểm khởi đầu tốt để hiểu cách thức hoạt động của đường ống giao diện người dùng. Hàng chuỗi giao diện người dùng cho TouchLatency bao gồm các màu khác nhau tại các thời điểm khác nhau. Các thanh biểu thị các trạng thái khác nhau cho luồng:
- Màu xám . Đang ngủ.
- Màu xanh da trời. Runnable (nó có thể chạy, nhưng bộ lập lịch chưa chọn nó để chạy).
- Màu xanh lá. Đang tích cực chạy (bộ lập lịch cho rằng nó đang chạy).
- Màu đỏ. Ngủ liên tục (thường ngủ trên một ổ khóa trong nhân). Có thể là dấu hiệu của tải I / O. Cực kỳ hữu ích để gỡ lỗi các vấn đề về hiệu suất.
- Quả cam. Ngủ liên tục do tải I / O.
Để xem lý do cho chế độ ngủ liên tục (có sẵn từ điểm theo sched_blocked_reason
), hãy chọn lát ngủ liên tục màu đỏ.
Trong khi EventThread đang chạy, chuỗi giao diện người dùng cho TouchLatency có thể chạy được. Để xem điều gì đã đánh thức nó, hãy nhấp vào phần màu xanh lam.

Hình 2 cho thấy chuỗi giao diện người dùng TouchLatency được đánh thức bởi tid 6843, tương ứng với EventThread. Chuỗi giao diện người dùng đánh thức, hiển thị khung và xếp hàng chờ 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, ở 15.423,65 ms Binder: 6832_1 trong SurfaceFlinger có thể chạy được vì tid 9579, là RenderThread của TouchLatency. Bạn cũng có thể thấy queueBuffer ở cả hai phía của giao dịch chất kết dính.
Trong hàng đợiBuffer ở phía SurfaceFlinger, số lượng khung hình đang chờ xử lý từ TouchLatency tăng từ 1 đến 2.

Hình 5 cho thấy bộ đệm ba lần, trong đó có hai khung đã hoàn thành và ứng dụng sắp bắt đầu hiển thị một phần ba. Điều này là do chúng tôi đã giảm một số khung hình, vì vậy ứng dụng giữ hai khung hình đang chờ xử lý thay vì một khung hình để cố gắng tránh các khung hình bị giảm thêm.
Ngay sau đó, luồng chính của SurfaceFlinger được đánh thức bởi EventThread thứ hai để nó có thể xuất khung đang chờ xử lý cũ hơn ra màn hình:

SurfaceFlinger đầu tiên chốt bộ đệm đang chờ xử lý cũ hơn, điều này làm cho số bộ đệm đang chờ xử lý giảm từ 2 xuống 1.

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

Tiếp theo, mdss_fb0
đánh thức trên CPU 0. mdss_fb0
là luồng nhân của đường ống hiển thị để xuất một khung được kết xuất ra màn hình. Chúng ta có thể thấy mdss_fb0
là hàng của chính nó trong dấu vết (cuộn xuống để xem).

mdss_fb0
thức dậy, chạy một thời gian ngắn, vào chế độ ngủ liên tục, sau đó thức dậy lại.
Ví dụ: Khung không hoạt động
Ví dụ này mô tả một hệ thống được sử dụng để gỡ lỗi Pixel / Pixel XL chập chờn. Để làm theo ví dụ này, hãy tải xuống tệp zip của các dấu vết (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 của bạn.
Khi bạn mở systrace, bạn sẽ thấy một cái gì đó như sau:

Khi tìm kiếm jank, hãy kiểm tra hàng FrameMissed trong SurfaceFlinger. FrameMissed là một cải tiế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ó tương quan với SurfaceFlinger thiếu một trong các thời gian chạy cực kỳ thường xuyên của nó và số bộ đệm đang chờ xử lý không thay đổi cho ứng dụng ( com.prefabulated.touchlatency
) tại một vsync.

Hình 11 cho thấy một khung hình bị nhỡ ở tốc độ 15598,29 & nbps; ms. SurfaceFlinger đã thức giấc một thời gian ngắn ở khoảng thời gian vsync và quay lại ngủ mà không thực hiện bất kỳ công việc nào, điều đó có nghĩa 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 đường ống bị hỏng cho khung này, trước tiên hãy xem lại ví dụ về khung làm việc ở trên để xem đường ống giao diện người dùng bình thường xuất hiện như thế nào trong systrace. Khi đã sẵn sàng, quay trở lại khung hình bị bỏ lỡ và làm việc lùi lại. Lưu ý rằng SurfaceFlinger 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 manh mối tốt để giúp tìm ra những gì đang xảy ra).

Bởi vì chúng tôi có khung trong SurfaceFlinger, đó không phải là vấn đề ứng dụng. Ngoài ra, SurfaceFlinger đang thức dậy vào đúng thời điểm, vì vậy nó không phải là vấn đề SurfaceFlinger. Nếu SurfaceFlinger và ứng dụng đều trông bình thường, có thể đó là vấn đề về trình điều khiển.
Bởi vì các điểm theo dõi sync
và mdss
được bật, chúng tôi có thể nhận thông tin về các 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 các khung được gửi đến màn hình. Các hàng rào này được liệt kê trong mdss_fb0_retire
, biểu thị thời điểm khung hiển thị. Các hàng rào này được cung cấp như một phần của 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 của bạn để hiểu ý nghĩa của các loại hàng rào trong dấu vết của bạn.

Hình 13 cho thấy một khung được hiển thị trong 33 ms, không phải 16,7 ms như mong đợi. Nửa đoạn đó, khung đó lẽ ra phải được thay thế bằng khung mới nhưng không phải vậy. Xem khung hình trước đó và tìm kiếm bất cứ thứ gì.

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

Dòng công việc chứa __vsync_retire_work_handler
, đang chạy khi hàng rào thay đổi. Nhìn qua nguồn hạt nhân, bạn có thể thấy rằng nó là một phần của trình điều khiển màn hình. Nó dường như nằm trên đường dẫn quan trọng cho đường dẫn hiển thị, vì vậy nó phải chạy càng nhanh càng tốt. Nó có thể chạy được cho 70 chúng tôi hoặc lâu hơn (không phải là thời gian trễ lên lịch lâu), nhưng đó là một dòng công việc và có thể không được lên lịch chính xác.
Kiểm tra khung trước đó để xác định xem điều đó có đóng góp hay không; đôi khi jitter có thể tăng lên theo thời gian và cuối cùng gây ra thời hạn trễ.

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