Bỏ qua đến nội dung chính
Về trang chủ
Tech tools-ai 10 phút đọc

Dịch Tễ Học Core Dump: OpenAI Đã "Chẩn Đoán" Và Khắc Phục Thành Công Lỗi 18 Năm Tuổi Trong Hạ Tầng Dữ Liệu AI Như Thế Nào? 🐛💡

OpenAI đã áp dụng phương pháp phân tích "dịch tễ học" trên quy mô lớn để xác định và sửa hai lỗi phức tạp, trong đó có một lỗi điều kiện tranh chấp 18 năm tuổi trong thư viện GNU libunwind, gây ra các sự cố khó hiểu trong hạ tầng dữ liệu C++ cốt lõi của họ.

Tier 1 · nguồn 99% độ tin cậy Auto-priority
Nguồn gốc openai.com

Chào mừng quý độc giả của Kalera News! 📰 Hôm nay, chúng ta sẽ cùng tìm hiểu về một câu chuyện gỡ lỗi đầy kịch tính tại OpenAI, nơi các kỹ sư đã "phẫu thuật" một lỗi phần mềm dai dẳng 18 năm tuổi bằng một phương pháp độc đáo: dịch tễ học core dump. Đây là câu chuyện về cách họ biến những sự cố tưởng chừng "bất khả thi" thành những vấn đề có thể giải quyết được.

Hạ Tầng Dữ Liệu C++ và Những "Cơn Đau Đầu" 🤯

Các mô hình và tác nhân của OpenAI ngày càng phụ thuộc vào hạ tầng dữ liệu mở rộng để tìm kiếm thông tin liên quan. Một phần lớn trong số này được viết bằng C++, ngôn ngữ cho phép kiểm soát cấp thấp để tối đa hóa hiệu suất và giảm thiểu việc sử dụng bộ nhớ. Tuy nhiên, sự thiếu an toàn bộ nhớ của C++ cũng là con dao hai lưỡi, khiến các lỗi nhỏ có thể gây ra những sự cố nghiêm trọng (crash) bằng cách ghi vào các địa chỉ bộ nhớ không chính xác.

Cách đây vài tháng, OpenAI bắt đầu ghi nhận các sự cố từ dịch vụ Rockset—một phần quan trọng của hạ tầng dữ liệu ChatGPT, được sử dụng cho nhiều plugin và tìm kiếm trong các cuộc hội thoại. Các sự cố này có chung một kịch bản kỳ lạ: một hàm C++ bình thường dường như kết thúc, sau đó trả về một địa chỉ "ma" khiến nhân hệ điều hành (kernel) phải dừng chương trình. Đôi khi, địa chỉ trả về trong stack frame lại là NULL. Những hiện tượng này hoàn toàn bất thường và khiến đội ngũ kỹ sư bế tắc. Mọi giả thuyết, kể cả những gì ChatGPT có thể nghĩ ra, đều không có bằng chứng đủ mạnh để chứng minh.

Điều mà họ cho là một vấn đề duy nhất, cuối cùng lại hóa ra là hai lỗi không liên quan, tình cờ được phát hiện cùng lúc.

Phương Pháp Gỡ Lỗi Truyền Thống: Phân Tích Từng "Bệnh Án" 🔬

Ban đầu, các kỹ sư của OpenAI tiếp cận vấn đề theo kiểu của một "bác sĩ": kiểm tra kỹ lưỡng một vài bản core dump (ảnh chụp trạng thái chương trình khi bị crash), đưa ra giả thuyết và loại bỏ từng cái một. Hầu hết các sự cố đều xảy ra trong phương thức DocumentTree::updateDocument. Dường như updateDocument đã gọi một hàm X không xác định, stack bị hỏng khi X đang hoạt động, rồi X trả về một địa chỉ không phải là mã thực thi.

Vấn đề là DocumentTree::updateDocument là một phương thức lớn, với nhiều phần được "inlining" (nhúng trực tiếp vào mã), khiến không gian tìm kiếm nguyên nhân trở nên khổng lồ. Việc các stack trace bị hỏng hoặc thiếu thông tin cũng khiến việc phân loại lỗi từ nhật ký trở nên vô cùng khó khăn. Họ thậm chí đã dành nhiều ngày để đào sâu vào một sự cố cụ thể, nhưng việc "ôm khư khư" suy nghĩ rằng tất cả các lỗi có cùng nguyên nhân đã khiến họ không thể thoát khỏi bế tắc.

BƯỚC NGOẶT: Từ "Bác Sĩ" Đến "Nhà Dịch Tễ Học" 📊

Đây chính là lúc OpenAI nhận ra cần một cách tiếp cận mới. Thay vì chỉ tập trung vào một "bệnh nhân" (tức là một sự cố cụ thể) như một bác sĩ, họ quyết định hành động như một nhà dịch tễ học: phân tích toàn bộ "quần thể" sự cố để tìm ra các mô hình mà một trường hợp đơn lẻ không thể tiết lộ.

Để làm được điều này, OpenAI đã đầu tư xây dựng một quy trình tự động phân tích hàng loạt core dump. Họ sử dụng một script (do ChatGPT viết!) để tải một phần của mỗi file core, trích xuất các thanh ghi (registers), lọc bỏ các lỗi dương tính giả đã biết, và tự động gán nhãn cho sự cố là "trả về NULL", "stack lệch", hoặc "khác". Script này được chạy song song trên tất cả các core dump của Rockset từ năm trước.

Đây chính là điểm mấu chốt của cuộc điều tra! Khi có được một bộ dữ liệu sạch, các mối tương quan bắt đầu xuất hiện ngay lập tức. Điều mà họ tưởng là một lỗi kỳ lạ hóa ra lại là hai nhóm sự cố hoàn toàn riêng biệt.

#### Lỗi Số 1: Phần Cứng "Hư Hỏng" 🛠️

Các sự cố "stack lệch" (misaligned-stack) có một đặc điểm rõ ràng: tất cả đều đến từ một khu vực cụ thể, có ngày bắt đầu rõ ràng và không bao giờ xảy ra trên các node đã chạy lâu. Mặc dù liên quan đến nhiều máy ảo Azure, nhưng mô hình này chỉ ra một máy vật lý duy nhất với phần cứng bị lỗi, gây ra sự cố cho bất kỳ máy ảo nào được triển khai trên đó.

Với danh sách các node Kubernetes và thời gian chính xác, OpenAI dễ dàng truy vết và đưa máy chủ vật lý bị lỗi vào danh sách đen. Mặc dù không thể tái tạo lỗi trên môi trường kiểm soát, nhưng các sự cố "stack lệch" đã biến mất hoàn toàn sau khi máy chủ đó ngừng hoạt động.

OpenAI đã cải thiện bộ xử lý tín hiệu lỗi để ghi nhận trạng thái thanh ghi, giúp phát hiện lỗi tương tự từ nhật ký mà không cần core dump. Họ cũng thay đổi cách quản lý máy ảo để ưu tiên tái sử dụng hơn là tạo mới, giúp việc phát hiện các node xấu dễ dàng hơn.

#### Lỗi Số 2: Sai Lầm 18 Năm Tuổi Trong GNU Libunwind 🤯

Sau khi loại bỏ các sự cố do phần cứng, các bản core dump "trả về NULL" còn lại trở nên dễ hiểu hơn nhiều. Hóa ra, tất cả các sự cố này đều xảy ra trong quá trình giải nén ngoại lệ (exception unwinding).

Khi C++ ném một ngoại lệ, runtime phải tìm ra khối catch nào sẽ nhận nó và những bộ hủy (destructors) hoặc bộ xử lý dọn dẹp nào nên chạy trên đường đi. Quá trình này được thực hiện bởi các hàm trợ giúp trong thư viện runtime. Thư viện GNU libunwind là một trong những ứng viên, và chính nó đã được hệ thống của OpenAI chọn sử dụng.

Kiểm tra mã nguồn GNU libunwind, các kỹ sư phát hiện ra rằng nó tổng hợp một cấu trúc ucontext_t trên stack, điền trạng thái thanh ghi mong muốn và chuyển con trỏ tới cấu trúc này cho một đoạn mã assembly nội bộ: _Ux86_64_setcontext.

Vấn đề nằm ở đây! Đoạn mã assembly này cập nhật thanh ghi rsp (con trỏ stack) đầu tiên. Ngay sau đó, cấu trúc ucontext_t không còn là một phần của stack hoạt động (hoặc vùng "red zone" được bảo vệ của kernel). Nếu một tín hiệu (như SIGUSR2 mà Rockset sử dụng rất thường xuyên) đến vào đúng khoảnh khắc đó, kernel sẽ xây dựng frame tín hiệu tại %rsp-128, có thể ghi đè lên bộ nhớ mà rdi đang trỏ tới. Nếu điều này xảy ra trước khi lệnh tiếp theo đọc UC_MCONTEXT_GREGS_RIP(%rdi) (con trỏ lệnh cần phục hồi), thì con trỏ lệnh được khôi phục có thể bị hỏng, và trong trường hợp này, nó bị ghi đè thành NULL. Đây chính là lỗi điều kiện tranh chấp 18 năm tuổi!

Thật khó tin, cửa sổ tranh chấp này chỉ rộng đúng một lệnh máy! Các kỹ sư ước tính rằng với tốc độ ngoại lệ cao (10.000 ngoại lệ/giây) và tín hiệu SIGUSR2 thường xuyên (100 lần/giây) của Rockset, xác suất xảy ra lỗi này là 1 trên 100 triệu cho mỗi lần giải nén ngoại lệ. Tuy nhiên, ở quy mô của OpenAI, điều này có nghĩa là một máy chủ có thể bị crash cứ vài giờ một lần, đủ để giải thích tần suất sự cố được quan sát trên toàn hệ thống.

Vậy tại sao lỗi này lại xuất hiện bây giờ sau 18 năm? Tốc độ crash tỷ lệ thuận với số lượng ngoại lệ được ném, số lượng tín hiệu được gửi, và mức độ sử dụng stack của bộ xử lý tín hiệu. Rockset đặc biệt ở cả ba khía cạnh này. Quan trọng hơn, một thay đổi đầu năm nay đã khiến bộ xử lý SIGUSR2 sử dụng nhiều stack hơn, điều này đã đẩy "sản phẩm" của tốc độ ngoại lệ, tốc độ tín hiệu và mức độ sử dụng stack vượt ngưỡng và khiến lỗi này trở nên rõ ràng về mặt vận hành.

OpenAI đã chuyển sang sử dụng bộ giải nén của libgcc thay vì GNU libunwind và đã gửi một bản vá cho dự án GNU libunwind gốc.

Sức Mạnh Của Chẩn Đoán Cấp Độ Tổng Thể 💪

Hành trình gỡ lỗi này đã mang lại nhiều bài học giá trị về liên kết động, siêu dữ liệu DWARF, phân phối tín hiệu Linux, ABI System V và cơ chế ngoại lệ C++. Nhưng bài học quan trọng nhất lại đơn giản hơn: việc xây dựng một bộ dữ liệu chất lượng cao chính là chìa khóa.

Nếu không có bộ dữ liệu này, OpenAI sẽ tiếp tục "trộn lẫn" hai hiện tượng khác nhau vào một câu chuyện duy nhất và vật lộn trong sự bối rối. Khi có dữ liệu chính xác và đầy đủ về toàn bộ "quần thể" sự cố, cấu trúc vấn đề trở nên rõ ràng: một nhóm sự cố thuộc về một máy chủ bị lỗi, và nhóm kia thuộc về một điều kiện tranh chấp trong libunwind. Khi dữ liệu tốt hơn, việc gỡ lỗi trở nên dễ dàng hơn.

Đối với các hệ thống hạ tầng như Rockset, điều này cực kỳ quan trọng. Cuộc điều tra này đã củng cố cam kết của OpenAI đối với việc trang bị công cụ sâu rộng, điều tra tự động và cải tiến liên tục trong các công cụ vận hành. Độ tin cậy không chỉ là sửa lỗi sau khi chúng xảy ra, mà còn là xây dựng dữ liệu, quy trình làm việc và kỹ năng biến những vấn đề tưởng chừng "bất khả thi" thành những vấn đề có thể chẩn đoán và giải quyết được. Kalera News tin rằng đây là một bài học quý giá cho bất kỳ đội ngũ kỹ thuật nào! 🚀