Hiểu về Systrace

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 cũng như cơ chế theo dõi chính trong nhân Linux. systrace sử dụng atrace để kích hoạt tính năng theo dõi, sau đó đọc bộ đệ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ợ cho 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à những gì đã được sử dụng trên Pixel/Pixel XL.)

systrace được sở hữu bởi nhóm Google Android và Google Chrome và là nguồn mở như một phần của dự án Catapult . Ngoài systrace, Catapult còn có các tiện ích hữu ích khác. Ví dụ: ftrace có nhiều tính năng hơn mức mà systrace hoặc atrace có thể kích hoạt trực tiếp 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 root và thường là kernel 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 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 lớn để tránh mất sự kiện (vì 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 systrace, hãy nhớ rằng mọi sự kiện đều được kích hoạt bởi thứ gì đó trên CPU .

Bởi vì systrace được xây dựng dựa trên ftrace và ftrace chạy trên CPU, nên một cái gì đó trên CPU phải ghi vào 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 trong 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 systrace.

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

Ví dụ này mô tả một systrace cho một quy trình giao diện người dùng thông thường. Để làm theo ví dụ, hãy tải xuống tệp zip chứ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. Xin lưu ý rằng systrace 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 duy nhất trước đây.

Để có khối lượng công việc nhất quán, định kỳ, chẳng hạn như TouchLatency, quy trình giao diện người dùng chứa những nội dung sau:

  1. 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.
  2. Ứ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.
  3. Ứng dụng sẽ gửi khung được hiển thị tới SurfaceFlinger bằng chất kết dính, sau đó SurfaceFlinger chuyển sang chế độ ngủ.
  4. EventThread thứ hai trong SurfaceFlinger đánh thức SurfaceFlinger để kích hoạt kết quả tổng hợp và hiển thị. 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ủ.
  5. SurfaceFlinger xử lý thành phần bằng Trình soạn thảo phần cứng (HWC)/Trình soạn thảo phần cứng 2 (HWC2) hoặc GL. Thành phần HWC/HWC2 nhanh hơn và tiêu thụ điện năng 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 khoảng 4-6 mili giây nhưng có thể trùng lặp với bước 2 vì ứng dụng Android luôn có bộ đệm gấp ba lần. (Mặc dù các ứng dụng luôn được đệm ba lần nhưng có thể chỉ có một khung hình đang chờ xử lý trong SurfaceFlinger, điều này khiến nó trông giống như bộ đệm đôi.)
  6. SurfaceFlinger gửi đầu ra cuối cùng để hiển thị với trình điều khiển của nhà cung cấp và quay lại chế độ ngủ, chờ đánh thức EventThread.

Hãy xem qua khung bắt đầu từ 15409 ms:

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

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 đây là điểm khởi đầu tốt để hiểu cách hoạt động của quy trình 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 vào 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 . Đang ngủ.
  • Màu xanh da trời. Có thể chạy được (nó có thể chạy, nhưng bộ lập lịch chưa chọn nó để chạy).
  • Màu xanh lá. Đang chạy tích cực (bộ lập lịch cho rằng nó đang chạy).
  • Màu đỏ. Giấc ngủ không bị gián đoạn (thường là ngủ trên một khóa trong kernel). Có thể biểu thị tải I/O. Cực kỳ hữu ích để gỡ lỗi các vấn đề về hiệu suất.
  • Quả cam. Giấc ngủ không bị gián đoạn do tải I/O.

Để xem lý do của chế độ ngủ không bị gián đoạn (có sẵn từ điểm theo dõi sched_blocked_reason ), hãy chọn lát cắt chế độ ngủ không bị gián đoạn màu đỏ.

Trong khi EventThread đang chạy, chuỗi giao diện người dùng cho TouchLatency sẽ 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.

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

Hình 2 cho thấy luồng UI 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ị một khung và xếp nó vào hàng đợi để SurfaceFlinger sử dụng.

Chuỗi giao diện người dùng đánh thức, hiển thị khung và xếp nó vào hàng đợi để SurfaceFlinger sử dụng
Hình 3. Chuỗi giao diện người dùng đánh thức, hiển thị một khung và xếp nó vào hàng đợi để SurfaceFlinger sử dụng

Nếu thẻ binder_driver được bật trong dấu vết, bạn có thể chọn 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 rằng, ở tốc độ 15.423,65 ms Binder:6832_1 trong SurfaceFlinger có thể chạy được nhờ tid 9579, tức là RenderThread của TouchLatency. Bạn cũng có thể thấy queueBuffer ở cả hai phía của giao dịch liên kết.

Trong queueBuffer ở phía SurfaceFlinger, số lượng khung hình đang chờ xử lý từ TouchLatency tăng từ 1 lên 2.

Các khung đang chờ xử lý đi từ 1 đến 2
Hình 5. Các khung đang chờ xử lý đi từ 1 đến 2

Hình 5 cho thấy bộ đệm ba lần, trong đó có hai khung hình đã hoàn thành và ứng dụng sắp bắt đầu hiển thị khung hình thứ ba. Điều này là do chúng tôi đã bỏ một số khung hình nên ứng dụng sẽ 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ị bỏ 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 hình cũ hơn đang chờ xử lý ra màn hình:

Chuỗi chính của SurfaceFlinger được đánh thức bởi EventThread thứ hai
Hình 6. Luồng chính của SurfaceFlinger được đánh thức bởi EventThread thứ hai

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

SurfaceFlinger lần đầu tiên bám vào bộ đệm đang chờ xử lý cũ hơn
Hình 7. SurfaceFlinger lần đầu tiên bám vào bộ đệm đang chờ xử lý cũ hơn

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

SurfaceFlinger thiết lập bố cục và gửi khung hình cuối cùng
Hình 8. SurfaceFlinger thiết lập bố cục và gửi khung hình cuối cùng

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

mdss_fb0 thức dậy trên CPU 0
Hình 9. mdss_fb0 thức dậy trên CPU 0

mdss_fb0 thức dậy, chạy một lúc, chìm vào giấc ngủ liên tục, rồi lại thức dậy.

Ví dụ: Khung không hoạt động

Ví dụ này mô tả một systrace dùng để gỡ lỗi jitter Pixel/Pixel XL. Để làm theo ví dụ, hãy tải xuống tệp zip chứ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ư thế này:

TouchLatency chạy trên Pixel XL với hầu hết các tùy chọn được bật
Hình 10. TouchLatency chạy trên Pixel XL (đã bật hầu hết các tùy chọn, bao gồm dấu vết mdss và kgsl)

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 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 việc SurfaceFlinger thiếu một trong những thời gian chạy cực kỳ đều đặn và số lượng bộ đệm đang chờ xử lý không thay đổi cho ứng dụng ( com.prefabulated.touchlatency ) ở vsync.

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

Hình 11 hiển thị khung bị bỏ lỡ ở 15598,29&nbps;ms. SurfaceFlinger thức dậy trong khoảng thời gian vsync và quay lại chế độ 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 để gửi lại 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 làm việc ở trên để xem quy trình 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, hãy quay lại khung bị bỏ lỡ và thực hiện 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 đầu mối tốt để giúp tìm hiểu điều gì đang diễn ra).

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

Vì chúng tôi có các khung trong SurfaceFlinger nên đây không phải là vấn đề về ứng dụng. Ngoài ra, SurfaceFlinger hoạt động vào đúng thời điểm nên đây không phải là vấn đề về SurfaceFlinger. Nếu SurfaceFlinger và ứng dụng đều trông bình thường thì đó có thể là sự cố trình điều khiển.

Vì các điểm theo dõi mdsssync được bật nên 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 hiển thị và SurfaceFlinger) kiểm soát thời điểm các khung được gửi tới màn hình. Những hàng rào này được liệt kê trong mdss_fb0_retire , biểu thị thời điểm một khung được hiển thị. Những hàng rào này được cung cấp như một phần của 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 tùy 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 danh mục hàng rào trong dấu vết của bạn.

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

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

Khung trước khung bị hỏng
Hình 14. Khung trước khung bị hỏng

Hình 14 hiển thị 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 đối với hai khung hình (chúng tôi kết xuất ở tần số 60 Hz, 16,7 mili giây trên mỗi khung hình, gần bằng). Nhưng 14,482 ms hoàn toàn không gần với 16,7 ms, cho thấy có điều gì đó không ổn với ống hiển thị.

Điều tra chính xác nơi hàng rào đó kết thúc để xác định điều gì kiểm soát nó.

Điều tra cuối hàng rào
Hình 15. Khảo sát đầu hàng rào

Hàng đợi 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 kernel, bạn có thể thấy nó là một phần của driver màn hình. Có vẻ như nó đang nằm trên đường dẫn quan trọng đối với quy trình hiển thị nên nó phải chạy càng nhanh càng tốt. Nó có thể chạy được trong khoảng 70 người (không phải là độ trễ lập kế hoạch lâu), nhưng đó là một hà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 khung đó có đóng góp hay không; đôi khi hiện tượng giật có thể tăng lên theo thời gian và cuối cùng khiến bạn bị trễ thời hạn.

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

Dòng có thể chạy trên kworker không hiển thị vì trình xem chuyển sang 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 ms độ trễ của bộ 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à xấu . 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ừ hàng công việc (chạy dưới dạng chuỗi SCHED_OTHER CFS) sang chuỗi kthread SCHED_FIFO chuyên dụng. Hàm này cần đả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à nguyên nhân gây ra vụ giật không? Thật khó để nói một cách thuyết phục. Ngoài các trường hợp dễ chẩn đoán, chẳng hạn như xung đột khóa nhân khiến các luồng quan trọng hiển thị ở chế độ ngủ, dấu vết thường không chỉ rõ vấn đề. Có thể hiện tượng giật hình này là nguyên nhân khiến khung hình bị rớt? Tuyệt đố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 mức đó trong các khung dẫn đến khung bị rơi. Do đường ống hiển thị được kết nối chặt chẽ như thế nào, có thể hiện tượng giật hình xung quanh thời gian của hàng rào đã dẫn đến khung hình bị rớt.

Trong ví dụ này, giải pháp liên quan đến việc chuyển đổi __vsync_retire_work_handler từ hàng công việc sang kthread chuyên dụng. Điều này dẫn đến những cải thiện đáng chú ý về độ giật và giảm độ 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 của hàng rào di chuyển rất gần 16,7 mili giây.