Lưu ý: đây là phần 3 của loạt bài về Azure Monitor Profiler. Phần 1 trình bày Profiler là gì, phần 2 trình bày cách bật nó. Bài viết này nói về phần thực sự quan trọng: hiểu rõ một trace khi bạn đã có nó.
Có profiler trace chỉ là một nửa công việc. Tôi đã thấy nhiều người bật profiler, mở một trace, nhìn chằm chằm vào một bức tường các tên phương pháp lạ lẫm, và đóng tab. Trình duyệt trace không tự giải thích được lần đầu tiên – tôi đã phải trải qua vài sự cố thực tế trước khi các quan điểm trở nên rõ ràng. Bài viết này là hướng dẫn mà tôi ước mình đã có.
Mục lục
Truy cập trace
Từ resource Application Insights của bạn:
- Đi tới tab Performance
- Chọn một operation từ danh sách hoặc để chọn Overall
- Nhấp vào Profiler traces
- Chọn một trong các request đã được chụp, lý tưởng là một request có thời lượng dài hơn bình thường – đó là nơi bạn thực sự tìm thấy điều gì đó.
Khi bạn đã ở trong một trace, bạn có hai chế độ xem cùng một dữ liệu call stack:
- Flame graph – toàn bộ cấp bậc call stack, hiển thị dưới dạng các thanh chồng chồng. Các thanh rộng hơn có nghĩa là nhiều thời gian hơn được dành cho cuộc gọi đó. Đây là hình dạng mà hầu hết mọi người nhận ra.
- Profile tree – cùng thông tin dưới dạng cây có thể mở rộng thay vì biểu đồ. Đôi khi dễ quét hơn khi bạn đang tìm kiếm tên phương pháp cụ thể thay vì một điểm nóng trực quan.
Không có chế độ xem nào “chính xác hơn” – hãy sử dụng bất kỳ chế độ nào phù hợp với cách bạn suy nghĩ về vấn đề.
Bắt đầu với Hot path, không phải đầu cây
Đừng bắt đầu đọc từ trên xuống. Chọn Hot path, và trình duyệt nhảy thẳng đến nút lá lớn nhất – trong hầu hết các trường hợp, đó là bottleneck thực sự. Đây là tính năng hữu ích nhất trong toàn bộ công cụ, và nó được kích hoạt theo mặc định.
Khi bạn đã ở hot path, các cột sẽ kể câu chuyện còn lại:
- Event – tên hàm hoặc sự kiện. Bạn sẽ thấy sự pha trộn giữa code của riêng bạn và các sự kiện framework/phụ thuộc như SQL hoặc HTTP calls.
- Module – nơi sự kiện hoặc hàm đó nằm.
- Thread time – khoảng thời gian giữa bắt đầu và kết thúc của operation.
- Timeline / When – một biểu đồ trực quan của các mẫu trên toàn bộ thời lượng request, chia thành 32 bucket. Một thanh cao có nghĩa là nút đang tích cực sử dụng tài nguyên trong khoảng thời gian đó.
Học từ vựng
Nhãn trên nút hot cho bạn biết bạn đang xem loại bottleneck nào, và các nhãn không phải lúc nào cũng hiển nhiên lần đầu tiên bạn thấy chúng:
- CPU time – đơn giản: CPU đang bận thực thi các hướng dẫn của bạn. Đây là chi phí tính toán thực sự.
- AWAIT_TIME – code của bạn gặp
awaitvà bị chặn logic chờ một task hoàn thành, mặc dù không có thread nào thực sự chờ đợi nó. NếuAWAIT_TIMExuất hiện trong framework code thay vì code của bạn, đó thường là phần plumbing xung quanhawait(hoặc telemetry ghi lại nó) chứ không phải manh mối thực sự – tắt Framework dependencies ở đầu trang để lọc bỏ tiếng ồn đó và chỉ thấy code của bạn. - BLOCKED_TIME – code đang chờ một tài nguyên khác: một sync object, một thread khả dụng, một request hoàn thành.
- clr!JITutil_MonContention / clr!JITutil_MonEnterWorker – lock contention. Một thread đang giữ lock (một statement
lock,Monitor.Enter, một phương pháp[MethodImpl(MethodImplOptions.Synchronized)]) và thread khác đang chờ đợi nó. - clr!JIT_New / clr!JIT_Newarr1 – cấp phát object hoặc array. Những thứ này thường nhanh; nếu bất kỳ thứ nào trong đó đang tiêu tốn thời gian đáng chú ý, bạn có thể đang cấp phát mạnh ở đâu đó gần đó.
- clr!ThePreStub hoặc một phương pháp được đánh dấu [COLD] – chi phí JIT compilation hoặc code chưa được tối ưu hóa chạy lần đầu tiên trong process. Chỉ nên xuất hiện mỗi phương pháp mỗi process một lần – nếu nó xuất hiện trên request mà người dùng đang truy cập, routine warmup thực thi code path đó trước khi traffic đến là giải pháp.
- Unmanaged Async – native code hoặc async kiểu cũ mà Profiler không thể theo dõi qua các thread bằng ETW. Nếu bạn cần thêm thông tin ngoài nhãn, hãy tải file ETW và mở trong PerfView.
Tại sao điều này quan trọng?
Nói rằng hot path dẫn đến một nút hiển thị SqlCommand.Execute với thanh rộng và thread time cao. Đó không phải là chi phí CPU – đó là code của bạn đang chờ đợi một database round trip. So sánh điều đó với hot path bị chi phối bởi clr!JITutil_MonContention: triệu chứng bên ngoài giống nhau (request chậm), giải pháp hoàn toàn khác. Một là “tối ưu hóa hoặc cache query”, hai là “tìm ra cái gì khác đang giữ lock đó”.
Sự phân biệt đó là toàn bộ điểm của việc đọc trace thay vì đoán từ bên ngoài. Một request chậm và một request chậm trông giống nhau từ biểu đồ thời lượng của Application Insights. Chúng không trông giống nhau trong flame graph.
Profiler về bản chất là một CPU sampling profiler. Đối với các lần chờ liên quan đến I/O thực sự – một dependency chậm, database thực sự là vấn đề thay vì query của bạn – flame graph cung cấp cho bạn nhãn “waiting” nhưng không có nhiều chiều sâu beyond đó. Transaction Diagnostics / dependency tracking của Application Insights là công cụ tốt hơn để tìm hiểu tại sao dependency call đó bị chậm.
Ghi chú: Nếu tên phương pháp trong trace hiển thị dưới dạng địa chỉ bộ nhớ thay vì tên đọc được, build của bạn không phát hành debug symbols (file PDB) nơi Profiler có thể tìm thấy chúng. Đảm bảo pipeline CI/CD của bạn phát hành PDB cùng với deployment. Không có chúng, mọi thứ sau điểm này đều không đọc được.
Từ việc đọc một trace đến việc không cần đọc trace nữa
Mọi thứ ở trên là kỹ năng thủ công: bạn mở một trace, nhấn Hot path, và tự lý giải các nhãn. Điều đó ổn cho một sự cố, nhưng nó không mở rộng đến “kiểm tra mỗi request chậm mà ứng dụng của tôi tạo ra”.
Đó là mục đích của Code Optimizations. Đây là dịch vụ dựa trên AI hoạt động trên cùng dữ liệu Profiler – nó không cần bật bất kỳ thứ gì mới, nó chỉ phân tích các trace mà Profiler của bạn đã thu thập – và tự động hiển thị các bottleneck CPU và bộ nhớ, mà bạn không cần mở một trace nào.
Bạn truy cập nó từ Investigate > Performance > Code Optimizations trên resource Application Insights của mình, hoặc qua trang tổng quan tổng hợp tóm tắt các insights trên mọi subscription và resource Application Insights mà bạn có quyền truy cập – hữu ích nếu bạn đang theo dõi nhiều hơn một ứng dụng.
Mỗi insight cung cấp cho bạn:
- Mô tả về vấn đề hiệu suất, với affected call stack
- Chi phí CPU hoặc bộ nhớ được biểu thị dưới dạng tỷ lệ của trace: đối với bộ nhớ, phần trăm của tất cả các phân bổ được thực hiện trong trace đó; đối với CPU, phần trăm của tổng thời gian CPU có sẵn (ví dụ, trace 10 giây trên máy 4 core cung cấp cho bạn 40 CPU-seconds để làm việc, vì vậy insight nêu 5% đang chỉ vào khoảng 2 giây)
- Biểu đồ xu hướng cho thấy tác động của vấn đề đã thay đổi như thế nào theo thời gian
- Khuyến nghị được tạo bởi AI để khắc phục vấn đề
Ghi chú: nếu bạn không thấy bất kỳ insight nào, đó không nhất thiết là dấu hiệu xấu – thường chỉ có nghĩa là Code Optimizations chưa phát hiện bottleneck nào đáng để gắn cờ. Hãy tiếp tục kiểm tra thay vì giả định nó bị hỏng.



