Tìm hiểu về systrace

systrace là công cụ chính để phân tích hiệu suất của thiết bị Android. Tuy nhiên, API này thực sự là một trình 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 không gian người dùng theo dõi và thiết lập ftrace cũng như cơ chế theo dõi chính trong Linux nhân hệ điều hành. systrace sử dụng dấu vết để bật tính năng theo dõi, sau đó đọc vùng đệm ftrace và sẽ gói tất cả trong một trình xem HTML độc lập. (Mặc dù các nhân hệ điều hành mới hơn có hỗ trợ dành cho Bộ lọc gói Berkeley nâng cao (eBPF) của Linux, hãy tham khảo 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 rất lớn).

systrace thuộc sở hữu của nhóm Google Android và Google Chrome và nguồn mở như một phần của Dự án Stadia. Trong 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 so với khả năng kích hoạt trực tiếp của systrace hoặc Atrace và chứa một số chức năng nâng cao quan trọng để gỡ lỗi hiệu suất vấn đề. (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 nhân mới.)

Chạy systrace

Khi gỡ lỗi hiện tượng dao động trên Pixel/Pixel XL, hãy bắt đầu bằng các bước 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à màn hình hoạt động của quy trình, cho phép bạn theo dõi từ hoạt động đầu vào của người dùng đến khung đang hiển thị trên màn hình. Đặt dung lượng bộ nhớ đệm thành một mức lớn để tránh bị mất sự kiện (vì không có vùng đệm lớn, một số CPU không chứa sự kiện nào sau một điểm nào đó trong dấu vết).

Khi truy cập systrace, xin lưu ý rằng mỗi sự kiện do nội dung nào đó trên CPU kích hoạt.

Vì systrace được xây dựng dựa trên ftrace và ftrace chạy trên CPU, nội dung 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 muốn biết lý do tại sao 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 tính năng chạy trên CPU đã kích hoạt thay đổi đó trong nhật ký). Chiến dịch này là nền tảng của việc phân tích hiệu suất bằng systrace.

Ví dụ: Khung làm việc

Ví dụ này mô tả một systrace cho quy trình giao diện người dùng thông thường. Để theo dõi với ví dụ: tải xuống tệp zip của 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, rồi mở tệp systrace_tutorial.html trong trình duyệt. Hãy cảnh báo rằng systrace này là một tệp lớn; trừ khi bạn dùng systrace hàng ngày có thể đây là một dấu vết lớn hơn nhiều với nhiều thông tin hơn so với từng thấy trong từng dấu vế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 thành phần sau:

  1. 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 mới.
  2. Ứng dụng này kết xuất một khung trong luồng giao diện người dùng, RenderThread và hwuiTasks bằng cách sử dụng CPU và Tài nguyên GPU. Đây là phần lớn dung lượng được chi tiêu cho giao diện người dùng.
  3. Ứng dụng gửi khung đã kết xuất đến SurfaceFlinger bằng một liên kết, sau đó SurfaceFlinger chuyển sang chế độ ngủ.
  4. Sự kiện EventThread thứ hai trong SurfaceFlinger đánh thức SurfaceFlinger để kích hoạt cấu trúc và đầu ra hiển thị. Nếu SurfaceFlinger xác định rằng không có tác vụ nào để được xong, thiết bị sẽ trở lại chế độ ngủ.
  5. SurfaceFlinger xử lý cấu trúc bằng cách sử dụng Hardware Composer (HWC)/Hardware Trình soạn thảo 2 (HWC2) hoặc GL. Thành phần HWC/HWC2 nhanh hơn và tiết kiệm năng lượng hơn nhưng có một số hạn chế phụ thuộc vào hệ thống trên một chip (SoC). Quá trình này thường mất khoảng 4 – 6 mili giây, nhưng có thể trùng lặp với bước 2 vì Android các ứng dụng luôn được lưu ba lần vào vùng đệm. (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ỉ là một khung đang chờ xử lý đang chờ trong SurfaceFlinger, khung này sẽ xuất hiện giống hệt như lưu vào bộ đệm kép).
  6. SurfaceFlinger gửi kết quả cuối cùng để hiển thị bằng trình điều khiển của nhà cung cấp và tiếp tục quay lại chế độ ngủ, đang chờ EventThread đánh thức.

Hãy xem qua khung hình bắt đầu từ 15409 mili giây:

Quy trình giao diện người dùng thông thường với EventThread đang chạy
Hình 1. Quy trình giao diện người dùng thông thường, EventThread đang chạy

Hình 1 là một khung hình bình thường được bao quanh bởi các khung thông thường, điểm xuất phát để tìm 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 đối với TouchLatency bao gồm các màu khác nhau tại các thời điểm khác nhau. Biểu thị các thanh các trạng thái khác nhau cho luồng:

  • Xám. Ngủ.
  • Xanh dương. Có thể chạy (có thể chạy nhưng trình lập lịch biểu thì không chọn nó để chạy).
  • Xanh lục. Đang chạy (bộ lập lịch cho rằng nó đang đang chạy).
  • Màu đỏ. Giấc ngủ không gián đoạn (thường nằm trên khoá trong nhân hệ điều hành). Có thể cho biết tải I/O. Cực kỳ hữu ích khi gỡ lỗi các vấn đề về hiệu suất.
  • Cam. Chế độ ngủ không gián đoạn do tải I/O.

Để xem lý do dẫn đến trạng thái ngủ không gián đoạn (bạn có thể xem lý do trong sched_blocked_reason tracepoint), chọn nút màu đỏ không gián đoạn lát cắt giấc ngủ.

Khi EventThread đang chạy, luồng giao diện người dùng cho TouchLatency sẽ trở thành có thể chạy. Để xem yếu tố đã đánh thức chuông báo, hãy nhấp vào phần màu xanh dương.

Luồng giao diện người dùng cho TouchLatency
Hình 2. Luồng giao diện người dùng cho TouchLatency

Hình 2 cho thấy luồng giao diện người dùng TouchLatency được đánh thức bởi ide 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 và đưa vào hàng đợi để SurfaceFlinger sử dụng.

Luồng giao diện người dùng sẽ đánh thức, kết xuất một khung và thêm khung đó vào hàng đợi để SurfaceFlinger sử dụng
Hình 3. Luồng giao diện người dùng sẽ đánh thức, kết xuất một khung và thêm khung đó 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. Giao dịch liên kết

Hình 4 cho thấy, ở tốc độ 15.423,65 ms Binder:6832_1 trong SurfaceFlinger trở thành có thể chạy được nhờ ide 9579, đó là RenderThread của TouchLatency. Bạn cũng có thể xemqueBuffer ở cả hai bên của giao dịch liên kết.

TrongqueueBuffer ở phía SurfaceFlinger, số lượng yêu cầu đang chờ xử lý khung hình từ TouchLatency chuyển từ 1 đến 2.

Các khung hình đang chờ xử lý chuyển từ 1 sang 2
Hình 5. Các khung hình đang chờ xử lý chuyển từ 1 sang 2

Hình 5 minh hoạ bộ đệm ba lần, trong đó có hai khung hình hoàn chỉnh và quảng cáo sắp bắt đầu hiển thị một phần ba. Lý do là vì chúng tôi đã bỏ một số khung hình, vì vậy, ứng dụng sẽ giữ hai khung hình đang chờ xử lý thay vì một khung để cố gắng tránh số khung hình bị rớt thêm.

Ngay sau đó, luồng chính của SurfaceFlinger bị đánh thức bởi EventThread thứ hai, vì vậy nó có thể xuất khung hình cũ đang chờ xử lý lên màn hình:

Luồng chính của SurfaceFlinger được một EventThread thứ hai đánh thức
Hình 6. Luồng chính của SurfaceFlinger bị đánh thức trong một giây Chuỗi sự kiện

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

Trước tiên, SurfaceFlinger kết nối với bộ đệm đang chờ xử lý cũ hơn
Hình 7. Trước tiên, SurfaceFlinger kết nối với những ứng dụng đang chờ xử lý cũ vùng đệm

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

SurfaceFlinger thiết lập thành phần và gửi khung hoàn thiện
Hình 8. SurfaceFlinger thiết lập cấu trúc và gửi khung hình cuối cùng

Tiếp theo, mdss_fb0 sẽ đánh thức trên 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 lên màn hình. Chúng ta có thể thấy mdss_fb0 là hàng riêng trong dấu vết (cuộn xuống để chế độ xem).

mdss_fb0 bật trên CPU 0
Hình 9. mdss_fb0 bật trên CPU 0

mdss_fb0 thức dậy, chạy trong giây lát, chuyển sang giấc ngủ không gián đoạn, sau đó sẽ thức dậy trở lại.

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 dao động của Pixel/Pixel XL. Người nhận làm theo ví dụ, tải tệp zip xuống tệp dấu vết (bao gồm cả các dấu vết khác được đề cập trong ), 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 giao diện như sau:

Độ trễ cảm ứng chạy trên Pixel XL với hầu hết các tuỳ chọn đã bật
Hình 10. Độ trễ chạm chạy trên Pixel XL (hầu hết các lựa chọn) đã bật, bao gồm các điểm theo dõi mdss và kgsl)

Khi tì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 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 trường hợp, FrameMissed tương quan với SurfaceFlinger thiếu một trong thời gian chạy cực kỳ thông thường và số lượng vùng đệm đang chờ xử lý không thay đổi cho ứng dụng (com.prefabulated.touchlatency) khi đồng bộ hoá.

FrameMissed tương quan với SurfaceFlinger
Hình 11. FrameMissed tương quan với SurfaceFlinger

Hình 11 cho thấy một khung bị bỏ lỡ ở tốc độ 15598,29&nbps; mili giây. SurfaceFlinger thức dậy trong chốc lát lúc trong khoảng thời gian vsync và trở lại chế độ ngủ mà không làm gì cả, điều đó có nghĩa là SurfaceFlinger xác định rằng việc gửi một khung lên màn hình là không đáng một lần nữa. Tại sao?

Để hiểu quy trình ngừng hoạt động cho khung này như thế nào, trước tiên hãy xem lại ví dụ về khung làm việc ở trên để xem cách quy trình giao diện người dùng thông thường xuất hiện trong systrace. Khi đã sẵn sàng, hãy quay lại khung hình bị bỏ lỡ và thực hiện thao tá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 các khung đang chờ xử lý từ TouchLatency, thì có 2 khung (một gợi ý hữu ích để giúp đỡ xác định điều gì đang diễn ra).

SurfaceFlinger thức dậy và ngay lập tức chuyển sang chế độ ngủ
Hình 12. SurfaceFlinger khởi động và ngay lập tức chuyển sang giấc ngủ

Vì chúng ta có khung trong SurfaceFlinger nên đây 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, không phải là SurfaceFlinger vấn đề. Nếu cả SurfaceFlinger và ứng dụng đều có giao diện bình thường, đó có thể là trình điều khiển.

Vì các điểm theo dõi mdsssync đã được bật, chúng tôi 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) giúp kiểm soát thời điểm gửi khung hình đến màn hình. Những hàng rào này được liệt kê trong mdss_fb0_retire, tức là biểu thị khi một khung hình đang hiển thị trên màn hình. Những hàng rào này được cung cấp dưới dạng thuộc danh mục dấu vết 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, vì vậy làm việc với nhà cung cấp SOC của bạn để hiểu ý nghĩa của các danh mục hàng rào trong dấu vết.

mdss_fb0_re lui hàng rào
Hình 13. Hàng rào mdss_fb0_retire

Hình 13 cho thấy một khung hình hiển thị trong 33 mili giây, chứ không phải 16,7 mili giây như mong đợi. Nửa chừng lát cắt đó, khung đó đã được thay thế bằng một khung mới nhưng không được. Xem khung trước và tìm kiếm khung hình bất kỳ.

Khung trước khung hình bị tắt
Hình 14. Khung trước khung hình bị tắt

Hình 14 hiển thị 14,482 mili giây của một khung. Phân đoạn hai khung bị hỏng là 33,6 mili giây, đó là con số gần đúng chúng tôi mong đợi cho hai khung hình (chúng tôi kết xuất ở tốc độ 60 Hz, 16,7 mili giây mỗi khung hình (gần). Nhưng 14,482 mili giây không gần bằng 16,7 mili giây, cho thấy có sự cố gì đó với ống hiển thị.

Hãy tìm hiểu chính xác vị trí kết thúc của hàng rào đó để xác định xem đâu là biện pháp kiểm soát hàng rào đó.

Kiểm tra đầu hàng rào
Hình 15. Kiểm tra đầu hàng rào

Một hàng đợi công việc chứa __vsync_retire_work_handler là chạy khi hàng rào thay đổi. Khi xem xét nguồn 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. Có vẻ như yêu cầu đường dẫn cho quy trình hiển thị, vì vậy, đoạn mã phải chạy nhanh nhất có thể. Bây giờ có thể chạy cho khoảng 70 người dùng (không chậm trễ khi lập lịch), nhưng đó 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 trước để xác định xem điều đó có đóng góp hay không; đôi khi biến động có thể tăng dần theo thời gian và cuối cùng khiến lỡ thời hạn.

Khung trước
Hình 16. Khung trước

Đường kẻ có thể chạy trên kworker không hiển thị vì người xem xoay màu trắng khi được chọn, nhưng số liệu thống kê cho biết câu chuyện: 2,3 mili giây của trình lập lịch biểu độ trễ cho một phần đường dẫn quan trọng của quy trình hiển thị là không hợp lệ. 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 hiển thị đường dẫn quan trọng của quy trình làm việc từ một hàng đợi công việc (chạy dưới dạng SCHED_OTHER luồng CFS) đến một SCHED_FIFO chuyên dụng kthread. Hàm này cần đảm bảo thời gian mà hàng công việc không thể (và không cung cấp.

Đây có phải là nguyên nhân gây hiện tượng giật không? Thật khó để nói một cách chắc chắn. Bên ngoài các trường hợp dễ chẩn đoán, chẳng hạn như tranh chấp khoá kernel, gây ra vấn đề về màn hình vào giấc ngủ, dấu vết thường không chỉ rõ vấn đề. Có thể sự biến động này là nguyên nhân khiến khung hình bị rớt không? Hoàn toàn có thể. Chiến lược phát hành đĩa đơn thời gian hàng rào phải là 16,7 mili giây, nhưng chúng không gần với điều đó trong các khung hình dẫn đến khung hình bị rớt. Do đường ống hiển thị được kết nối chặt chẽ như thế nào có thể sự biến động xung quanh hàng rào xung quanh đã dẫn đến sự sụt giảm khung.

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 hàng đợi công việc đến kthread. Điều này dẫn đến những cải thiện đáng kể về hiện tượng dao động và giảm hiện tượng giật trong bài kiểm tra bóng nảy. Các dấu vết tiếp theo cho thấy thời gian hàng rào rất gần lên 16,7 mili giây.