Static Analysis Là Gì?
Phân tích tĩnh – Static Analysis liên quan đến việc kiểm thử mà không thực sự thực thi phần mềm đang được kiểm thử và có thể liên quan đến code hoặc kiến trúc hệ thống. Phân tích tĩnh thường được thực hiện bằng công cụ hỗ trợ. Tuy nhiên, đôi khi chúng ta có thể thực hiện phân tích tĩnh theo cách thủ công.
Các bài viết tiếp theo mình sẽ giới thiệu lần lượt về các chủ đề như phân tích luồng điều khiển (control-flow analysis), phân tích luồng dữ liệu (data-flow analysis), tuân thủ các tiêu chuẩn, một số metric code nhất định, và biểu đồ cuộc gọi (call-graphing).
Nào hãy cùng tìm hiểu với mình nhé.
Complexity Analysis
Trong phân tích tĩnh (Static Analysis), Complexity Analysis được sử dụng để đo lường độ phức tạp của source code mà không cần thực thi chương trình. Điều này giúp đánh giá mức độ phức tạp của code và xác định các vấn đề có thể xảy ra hoặc cần được tinh chỉnh để cải thiện hiệu suất, bảo trì hoặc sửa lỗi.
Các phương pháp phân tích phức tạp thường bao gồm:
Độ phức tạp thuật toán (Algorithmic Complexity):
- Thời gian chạy: Ước lượng thời gian thực thi của một thuật toán dựa trên đầu vào. Các phân tích Big O notation thường được sử dụng để đánh giá sự tăng trưởng của thời gian chạy khi kích thước đầu vào tăng lên.
- Không gian bộ nhớ: Đo lường sự tiêu tốn bộ nhớ của một thuật toán, bao gồm đánh giá về việc sử dụng bộ nhớ tạm thời, biến và cấp phát bộ nhớ.
Độ phức tạp cấu trúc code (Code Structure Complexity):
- Độ phức tạp cấu trúc: Đo lường mức độ phức tạp của source code dựa trên các yếu tố như số lượng lệnh, khối lệnh lồng nhau, điều kiện phân nhánh, vòng lặp, và sự phụ thuộc giữa các thành phần.
- Mức độ phức tạp của hàm và phương thức: Đánh giá mức độ phức tạp của từng hàm hoặc phương thức trong source code, bao gồm số lượng lệnh, độ phức tạp của logic, và sự phụ thuộc.
Chất lượng Source Code (Source Code Quality):
- Sử dụng biến và tên gọi đúng cách: Đánh giá xem biến và tên gọi có mô tả rõ ràng, dễ hiểu không, hay có tiêu chuẩn chung không.
- Kiểm tra cấu trúc điều kiện: Đánh giá các điều kiện logic, bao gồm điều kiện phức tạp, nhánh điều kiện không cần thiết, và khả năng đọc hiểu logic của nó.
Các Tool Thường Sử Dụng
Tất cả các công cụ phân tích độ phức tạp trong phân tích tĩnh source code có mục tiêu chung là đánh giá chất lượng của code, phát hiện các vấn đề tiềm ẩn, và cung cấp gợi ý để cải thiện source code. Mỗi tool có ưu điểm và hạn chế riêng, tùy thuộc vào ngôn ngữ lập trình và mục tiêu cụ thể của dự án, việc chọn công cụ phù hợp sẽ giúp tối ưu hóa quá trình phân tích và cải thiện chất lượng của source code. Một số tool phổ biến trong phân tích tĩnh như sau:
1. ESLint:
- Chức năng: Dành cho code JavaScript hoặc TypeScript.
- Tính năng: Xác định các vấn đề về định dạng code, quy tắc chuẩn mã hóa, và độ phức tạp của code. ESLint cung cấp các tùy chọn cấu hình mạnh mẽ để tuỳ chỉnh quy tắc theo nhu cầu cụ thể của dự án.
2. Pylint:
- Chức năng: Dành cho code Python.
- Tính năng: Đánh giá chất lượng của code Python bằng cách xác định các vấn đề về định dạng, chuẩn mã hóa và độ phức tạp của code. Nó cung cấp các báo cáo chi tiết về các cảnh báo và lỗi tiềm ẩn.
3. SonarQube:
- Chức năng: Hỗ trợ nhiều ngôn ngữ lập trình.
- Tính năng: Đánh giá chất lượng source code bằng cách phân tích độ phức tạp, chuẩn mã hóa, mã hóa bảo mật và cung cấp báo cáo chi tiết với khả năng tùy chỉnh mạnh mẽ.
4. Checkstyle:
- Chức năng: Dành cho source code Java.
- Tính năng: Kiểm tra và đánh giá cấu trúc code Java, bao gồm các quy tắc chuẩn mã hóa và các vấn đề về độ phức tạp của code.
5. RuboCop:
- Chức năng: Kiểm tra source code Ruby.
- Tính năng: Đánh giá cú pháp và độ phức tạp của code Ruby, cung cấp các gợi ý và cảnh báo để cải thiện chất lượng code.
6. Cppcheck:
- Chức năng: Dành cho source code C/C++.
- Tính năng: Phân tích code C/C++ để phát hiện lỗi logic, vấn đề về độ phức tạp code và cung cấp gợi ý sửa lỗi.
7. FindBugs:
- Chức năng: Phân tích source code Java.
- Tính năng: Phát hiện các lỗi tiềm ẩn và vấn đề về độ phức tạp của code Java, đồng thời cung cấp các gợi ý để sửa lỗi.
8. Bandit:
- Chức năng: Tìm lỗi bảo mật trong source code Python.
- Tính năng: Đánh giá code Python để phát hiện các lỗ hổng bảo mật và vấn đề về độ phức tạp của code.
Các Tiêu Chuẩn Và Hướng Dẫn
Các lập trình viên đôi khi coi các tiêu chuẩn và hướng dẫn lập trình là lãng phí và khó chịu. Chúng ta có thể hiểu quan điểm này bởi các nhà phát triển thường bị hạn chế nghiêm trọng về thời gian và nguồn lực. Code được viết tốt hơn và được ghi chép tốt hơn sẽ mất nhiều thời gian hơn để viết. Và có thể đôi khi các tiêu chuẩn và hướng dẫn quá khắt khe. Người kiểm thử cần nhắc nhở các nhà phát triển rằng, về lâu dài, khả năng bảo trì hệ thống cao hơn sẽ tiết kiệm được thời gian và công sức sau này.
Có một số công cụ phân tích tĩnh có thể được tùy chỉnh để cho phép thực thi các tiêu chuẩn và hướng dẫn nội bộ. Và, một số tính năng có thể bị tắt vào một số thời điểm nhất định nếu điều đó có ý nghĩa về mặt kinh tế hoặc chiến lược.
Một số tiêu chuẩn và nguyên tắc bạn có thể tìm thấy trên công cụ phân tích Checkstyle. Công cụ nguồn mở này được tạo ra vào năm 2001 và hiện đang được sử dụng trên toàn thế giới.
- Kiểm tra các chú thích Javadoc được nhúng. Javadoc là một giao thức nhúng tài liệu vào code có thể được phân tích cú pháp và hiển thị ở định dạng HTML. Về cơ bản, đây là tính năng cho phép toàn bộ tổ chức viết tài liệu chính thức cho hệ thống. Tất nhiên, nó chỉ hoạt động khi mọi nhà phát triển đều đưa nó vào code của mình.
- Thực thi các quy ước đặt tên để tạo code tự ghi. Ví dụ: các hàm có thể được yêu cầu đặt tên là động từ hành động với kiểu dữ liệu trả về được mã hóa trong đó (ví dụ: strCalculateHeader(), recPlotPath(), v.v.). Các hằng số có thể được yêu cầu phải là tất cả chữ hoa. Các biến có thể có kiểu dữ liệu được mã hóa. Có nhiều quy ước đặt tên khác nhau đã được sử dụng; nếu mọi người trong tổ chức đều tuân theo các tiêu chuẩn và nguyên tắc thì việc hiểu code của mọi người sẽ dễ dàng hơn nhiều, ngay cả khi bạn không viết nó.
- Tự động kiểm tra độ phức tạp theo chu kỳ của một quy trình so với giới hạn được chỉ định.
- Đảm bảo rằng các tiêu đề bắt buộc đã được đặt đúng chỗ. Chúng thường được thiết kế để giúp người dùng chọn đúng quy trình để sử dụng trong code của riêng họ thay vì viết lại chúng. Thông thường các tiêu đề sẽ kèm theo danh sách tham số, giá trị trả về, tác dụng phụ, v.v..
- Kiểm tra những con số kỳ diệu. Đôi khi các nhà phát triển sử dụng các giá trị không đổi thay vì các hằng số được đặt tên trong code của họ. Điều này đặc biệt có hại khi giá trị thay đổi vì tất cả các lần xuất hiện cũng phải được thay đổi. Việc sử dụng hằng số được đặt tên cho phép tất cả thay đổi xảy ra cùng một lúc vì thay đổi được thực hiện ở một vị trí duy nhất.
- Kiểm tra khoảng trắng (hoặc thiếu khoảng trắng) xung quanh các code thông báo hoặc ký tự nhất định. Điều này mang lại cho code một giao diện chuẩn hóa và thường làm cho code dễ hiểu hơn.
- Thực thi các quy ước đã được thống nhất chung trong code. Ví dụ: có một tài liệu tên là “Quy ước code cho ngôn ngữ lập trình Java.” Tài liệu này phản ánh các tiêu chuẩn mã hóa ngôn ngữ Java được trình bày trong Đặc tả ngôn ngữ Java19 của Sun Microsystems. Như vậy, nó có thể là thứ gần nhất với tiêu chuẩn mà Java có. Kiểu kiểm tra có thể được đặt để thực thi các quy ước này, bao gồm các chi tiết về cách xây dựng một lớp một cách chính xác.
- Tìm kiếm code để tìm code trùng lặp. Điều này được thiết kế để ngăn cản các hoạt động sao chép và dán, thường được coi là một kỹ thuật thực sự tồi để sử dụng. Nếu lập trình viên cần làm điều tương tự nhiều lần, anh ta nên cấu trúc lại nó thành một quy trình có thể gọi được.
- Kiểm tra các vấn đề về tầm nhìn. Một trong những tính năng của thiết kế hướng đối tượng là khả năng ẩn các phần code. Một hệ thống hướng đối tượng được phân lớp với ý tưởng giữ các lớp tách biệt. Nếu chúng ta biết một lớp được thiết kế như thế nào, chúng ta có thể sử dụng một số kiến thức đó khi bắt nguồn từ lớp đó. Tuy nhiên, việc sử dụng kiến thức đó có thể trở thành vấn đề nếu chủ sở hữu của lớp đó quyết định thay đổi code. Những thay đổi đó cuối cùng có thể phá vỡ code. Nói chung, khi chúng ta tạo ra một lớp mới, chúng ta không nên biết gì về siêu lớp ngoài những gì chủ sở hữu của nó quyết định chỉ cho chúng ta một cách rõ ràng. Lỗi hiển thị xảy ra khi một lớp không được thiết kế chính xác, hiển thị nhiều hơn mức cần thiết.
Đây chỉ là một số vấn đề mà loại công cụ phân tích tĩnh này có thể xác định.
Kết Luận
Tóm lại, code càng phức tạp thì càng dễ có lỗi. Đôi khi độ phức tạp cao là điều cần thiết. Khi tổ chức của chúng ta cố gắng viết một số module hệ điều hành quan trọng bằng cách sử dụng phương pháp có độ phức tạp thấp, tốc độ thực thi quá chậm. Khi họ loại bỏ cấu trúc đẹp mắt và chỉ viết code có độ phức tạp cao, được liên kết chặt chẽ, nó sẽ thực thi đủ nhanh theo nhu cầu của họ. Chắc chắn rằng việc bảo trì nó rất có vấn đề; phải mất nhiều thời gian hơn để đạt được chất lượng cao hơn so với các module khác mà họ đã viết. Như một nhà thông thái đã từng nói: “Bạn trả tiền và bạn đưa ra lựa chọn của mình”.
Mình xin dừng bài viết hôm nay tại đây. Hẹn gặp lại các bạn trong các bài viết tiếp theo.
Happy Testing!