Debug (chế độ gỡ lỗi) — là một cấu hình xây dựng ứng dụng di động trong đó trình biên dịch bao gồm thông tin ký hiệu, tắt tối ưu hóa mã và kết nối trình gỡ lỗi để phân tích thực thi từng bước. Theo Android Developers, bản dựng Debug chứa các ký hiệu gỡ lỗi, không nén tài nguyên và cho phép kết nối trình kiểm tra cơ sở dữ liệu và yêu cầu mạng. Chế độ Debug đối lập với bản dựng Release: trong Debug, nhà phát triển hy sinh hiệu suất để có sự minh bạch trong thực thi mã.
Những điểm chính
Debug không chỉ là một cờ trình biên dịch, mà là một tập hợp đầy đủ các cài đặt giúp ứng dụng trở nên minh bạch cho nhà phát triển. Trong chế độ Debug, trình biên dịch thêm một bảng tên ký hiệu (DWARF) vào tệp thực thi, liên kết mã máy với các dòng mã nguồn. Không có bảng này, trình gỡ lỗi không thể hiển thị dòng mã nào đang được thực thi tại thời điểm hiện tại.
Trình gỡ lỗi (debugger) là một chương trình chạy ứng dụng của bạn trong môi trường được kiểm soát. Bạn có thể tạm dừng thực thi tại bất kỳ dòng nào (breakpoint), xem giá trị của tất cả các biến trong phạm vi hiện tại, thay đổi chúng khi đang chạy và tiếp tục thực thi. Đối với nền tảng di động, trình gỡ lỗi tiêu chuẩn là LLDB — một thành phần LLVM được sử dụng trong cả Xcode và Android Studio.
Chế độ Debug cũng bao gồm các kiểm tra bổ sung bị tắt trong Release: xác nhận (assertions), kiểm tra ranh giới mảng, trình phát hiện rò rỉ bộ nhớ và ghi nhật ký mở rộng. Các kiểm tra này làm chậm ứng dụng nhưng phát hiện lỗi ở giai đoạn đầu phát triển — trước khi mã đến tay người dùng.
Sự khác biệt giữa bản dựng Debug và Release là cơ bản: chúng là hai bộ cờ trình biên dịch, cấu hình ký và cài đặt đóng gói khác nhau. Hiểu những khác biệt này giúp tránh các tình huống “chạy được trên trình mô phỏng nhưng không chạy được trên thiết bị thật”.
| Tham số | Debug | Release |
|---|---|---|
| Tối ưu hóa | Tắt (-O0) | Bật (-Os hoặc -O2) |
| Ký hiệu | Bảng DWARF đầy đủ | Đã xóa |
| Chữ ký | Chứng chỉ phát triển | Chứng chỉ phân phối |
| Hồ sơ | Hồ sơ cấp phép Debug | Hồ sơ App Store / Ad Hoc |
| Ghi nhật ký | Đầy đủ (tất cả cấp độ) | Tắt hoặc tối thiểu |
| Làm rối mã | Tắt | Bật (ProGuard/R8) |
| Kích thước .apk/.ipa | Lớn hơn (ký hiệu + không nén) | Nhỏ hơn (R8 + tài nguyên) |
Bản dựng Debug được sử dụng ở tất cả các giai đoạn phát triển và thử nghiệm trên thiết bị cục bộ. Bản dựng Release được tạo trước khi gửi lên App Store Connect hoặc Google Play Console. Gỡ lỗi trên bản dựng Release về mặt kỹ thuật có thể nhưng cực kỳ bất tiện do các phương thức bị đổi tên (R8) và thiếu symbolication cho nhật ký sự cố.
Một vấn đề phổ biến là mã hoạt động trong Debug nhưng bị lỗi trong Release. Nguyên nhân là UB (hành vi không xác định) trong mã mà trình biên dịch xử lý khác nhau với các mức tối ưu hóa khác nhau. Một ví dụ điển hình: đọc biến chưa được khởi tạo hoặc vi phạm strict aliasing. Để phát hiện các lỗi này, hãy sử dụng trình phân tích tĩnh (Clang Static Analyzer, ktlint) trước mỗi bản dựng Release.
LLDB là trình gỡ lỗi hiệu suất cao dựa trên LLVM, hỗ trợ C, C++, Objective-C, Swift và Kotlin/Native. LLDB cung cấp giao diện REPL nơi bạn có thể thực thi các biểu thức tùy ý, thay đổi giá trị biến và gọi hàm trong ngữ cảnh của ứng dụng bị tạm dừng.
Breakpoint là công cụ chính của trình gỡ lỗi. Bạn đặt một điểm trên một dòng mã và ứng dụng sẽ tạm dừng khi thực thi đến dòng đó. LLDB hỗ trợ nhiều loại breakpoint: có điều kiện (chỉ kích hoạt khi điều kiện được đáp ứng), ký hiệu (khi gọi hàm) và một lần (kích hoạt một lần và tự động bị xóa).
Watchpoint là điểm quan sát sự thay đổi của biến. Bạn chỉ định một địa chỉ bộ nhớ và trình gỡ lỗi tạm dừng thực thi khi có bất kỳ ghi nào vào địa chỉ đó. Công cụ này không thể thiếu khi tìm kiếm các cuộc đua dữ liệu và biến đổi sai của các đối tượng dùng chung. Để xem hệ thống phân cấp UIKit, hãy sử dụng UIView Inspector có trong Xcode.
// Đặt breakpoint có điều kiện
(lldb) breakpoint set --name "viewDidLoad" --condition "self.isViewLoaded == false"
// Watchpoint trên thuộc tính
(lldb) watchpoint set variable self->_loadingState
// Thực thi mã trong ngữ cảnh bị dừng
(lldb) expr self.view.backgroundColor = UIColor.redColor
Cả hai IDE đều cung cấp trình kiểm tra đồ họa trên nền LLDB. Android Studio bao gồm Layout Inspector (hệ thống phân cấp View), Network Inspector (theo dõi yêu cầu HTTP) và Database Inspector (SQLite thời gian thực). Xcode cung cấp Debug Memory Graph (phân tích rò rỉ bộ nhớ) và View Debugger (xem 3D các lớp UIKit).
Từ Android 11, gỡ lỗi qua Wi-Fi hoạt động mà không cần kết nối USB: chỉ cần quét mã QR từ Android Studio. iOS hỗ trợ gỡ lỗi Wi-Fi từ Xcode 9+ — thiết bị kết nối một lần qua USB, sau đó các phiên gỡ lỗi có thể chạy qua mạng. Gỡ lỗi Wi-Fi không phù hợp cho máy chủ CI do độ trễ không thể đoán trước và mất gói tin, vì vậy các đường ống tự động luôn sử dụng USB. Tuy nhiên, đối với phát triển cục bộ, gỡ lỗi Wi-Fi thuận tiện hơn đáng kể — nhà phát triển không bị ràng buộc bởi cáp và có thể thử nghiệm ứng dụng trên thiết bị ở đầu kia của căn phòng.
Android Debug Bridge (ADB) là một công cụ phổ quát để tương tác với thiết bị Android từ dòng lệnh. Qua ADB, bạn có thể cài đặt ứng dụng, bắt đầu gỡ lỗi, sao chép tệp, thực thi lệnh shell và xem nhật ký. Android Studio sử dụng ADB nội bộ cho tất cả các hoạt động gỡ lỗi.
Android Studio hỗ trợ hai chế độ gỡ lỗi: Run (khởi chạy bình thường) và Debug (khởi chạy với trình gỡ lỗi được kết nối). Trong chế độ Debug, bạn có thể đặt breakpoint trực tiếp trong trình soạn thảo, xem biến trong cửa sổ Debug Tool và đánh giá biểu thức trong Evaluate Expression. Để gỡ lỗi các tiến trình nền (Service, BroadcastReceiver), hãy sử dụng Attach Debugger to Android Process.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Breakpoint ở đây sẽ tạm dừng thực thi
val button = findViewById<Button>(R.id.btn_debug)
button.setOnClickListener {
startDebugProcess()
}
}
private fun startDebugProcess() {
val data = fetchDataFromApi()
Log.d("Debug", "Data loaded: $data")
}
}
Các lệnh shell ADB cung cấp quyền truy cập vào hệ thống tệp của thiết bị mà không cần quyền root. Bạn có thể xem nội dung của thư mục databases, sao chép tệp .db vào máy tính và mở nó bằng bất kỳ máy khách SQLite nào. Android Studio Database Inspector tự động hóa quy trình này: bạn thấy dữ liệu cơ sở dữ liệu trực tiếp theo thời gian thực và có thể thực thi truy vấn SQL trực tiếp từ IDE.
Xcode cung cấp môi trường gỡ lỗi tích hợp dựa trên LLDB. Nhà phát triển có thể chạy ứng dụng trên trình mô phỏng hoặc thiết bị vật lý, đặt breakpoint và sử dụng Debug Navigator để kiểm soát các luồng thực thi. Không giống như Android, iOS không cho phép chạy hai bản dựng Debug đồng thời trên cùng một thiết bị mà không có cấu hình đặc biệt.
Trình mô phỏng chạy ứng dụng như một tiến trình macOS gốc, cung cấp chu kỳ gỡ lỗi nhanh nhất. Trên thiết bị vật lý, gỡ lỗi diễn ra qua USB hoặc Wi-Fi (từ iOS 16) và LLDB giao tiếp với debugserver trên thiết bị. Hiệu suất gỡ lỗi trên thiết bị thấp hơn do băng thông USB 2.0 hạn chế, nhưng chỉ có thiết bị vật lý mới cho phép thử nghiệm các kịch bản thực tế: thông báo đẩy, máy ảnh, cảm biến.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
private func setupUI() {
let label = UILabel()
label.text = "Chế độ Debug"
label.textColor = .systemBlue
view.addSubview(label)
}
}
Xcode Organizer thu thập nhật ký sự cố từ thiết bị của người thử nghiệm qua Crash Logs. Để symbolication (chuyển đổi địa chỉ thành tên hàm), cần có tệp .dSYM, được tạo ra với mỗi bản dựng Debug. Trong bản dựng Release, dSYM cũng được tạo, nhưng nhật ký sự cố từ App Store cần được tải lên Organizer thủ công hoặc qua dịch vụ bitcode.
Các câu hỏi thường gặp
Về mặt kỹ thuật có — thông qua phân phối Ad Hoc với chứng chỉ Debug, nhưng Apple và Google không khuyến nghị điều này. Bản dựng Debug chứa các ký hiệu gỡ lỗi và hiệu suất thấp hơn, làm giảm trải nghiệm người dùng và tăng kích thước ứng dụng lên 2–3 lần.
Nguyên nhân là tối ưu hóa trình biên dịch bị tắt (-O0). Trình biên dịch không inline các hàm, không loại bỏ mã chết và giữ lại tất cả các biến trung gian. Ngoài ra, Debug bao gồm các kiểm tra xác nhận và ranh giới mảng mà không có trong Release.
Trong Xcode chọn Window → Devices and Simulators, đánh dấu “Connect via network” cho thiết bị của bạn. Thiết bị và Mac phải ở trên cùng một mạng Wi-Fi. Sau khi kết nối qua USB một lần, gỡ lỗi sẽ hoạt động qua Wi-Fi trong các lần khởi chạy tiếp theo.
Attach to process cho phép bạn kết nối trình gỡ lỗi với một tiến trình đang chạy mà không cần khởi động lại ứng dụng. Điều này hữu ích để gỡ lỗi Service, BroadcastReceiver hoặc các tiến trình được khởi động bởi sự kiện hệ thống, nơi Debug Run tiêu chuẩn không áp dụng được.
NSLog và print theo mặc định chỉ xuất nhật ký trong cấu hình Debug. Đối với Release, hãy sử dụng os_log với cờ OSLogType.default — nó lưu thông báo vào Unified Logging System và có thể truy cập qua Console.app trên Mac.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm