Giao diện với trình gỡ rối CDT, Phần 1: Hiểu về giao diện trình gỡ rối C/C++

Thêm các công cụ tuỳ chỉnh vào khung công tác của trình gỡ rối CDT

Eclipse C/C++ Development Tooling (CDT-Công cụ phát triển C/C++ Eclipse) là một trong những môi trường mã nguồn mở nổi tiếng nhất để phát triển C/C++ và trình gỡ rối với tính năng đầy đủ đóng một vai trò lớn cho những người sử dụng nó. Điều chưa được biết là khung công tác CDT có thể được mở rộng để hỗ trợ các trình gỡ rối tùy chỉnh. Với trình cắm thêm bên phải, một trình gỡ rối tùy chỉnh có thể truy cập toàn bộ dải của môi trường gỡ rối đồ họa của CDT: theo dõi từng bước của mã, điểm giám sát (watchpoints), các điểm ngắt (breakpoints), nội dung đăng ký, nội dung bộ nhớ và các khung nhìn thay đổi được. Hãy tìm hiểu cách xây dựng trình cắm thêm này, với trọng tâm là giao diện gỡ rối C/C++ (CDI).

Matthew Scarpino, Phát triển Java, Eclipse Engineering, LLC

Matthew Scarpino là nhà quản lý dự án và nhà phát triển Java tại Eclipse Engineering LLC. Ông là tác giả hàng đầu của SWT/JFace in Action và có đóng góp nhỏ nhưng quan trọng vào Standard Widget Toolkit (SWT). Ông thích nhạc dân gian Ailen, chạy maratông, thơ của William Blake, và Graphical Editing Framework (GEF).


Cấp độ đóng góp cho developerWorks của
        tác giả

23 10 2009

Một giao diện dòng lệnh là một công cụ dịch vụ để gỡ rối, nhưng chưa có gì tỏ ra là chuyên nghiệp và tinh tế như một môi trường đồ họa kỹ xảo cao (well-crafted). Phải mất thời gian và công sức đáng kể để xây dựng một môi trường gỡ rối có đầy đủ tính năng từ đầu, nhưng có thể có giải pháp khác: công cụ phát triển C/C++ Eclipse (CDT). Tính mở rộng của CDT cho phép bạn kết nối các khả năng gỡ rối đồ họa của nó với trình gỡ rối tuỳ chỉnh của mình. Điều này không đòi hỏi nhiều mã, mà yêu cầu bạn cần hiểu các điểm mở rộng của CDT và CDI.

CDI là một Java™-based Application Programming Interface (API - Giao diện lập trình ứng dụng dựa trên Java) mà các lớp và các giao diện của nó làm cho nó có thể truy cập vào khung công tác gỡ rối của CDT. Trình cắm thêm Eclipse sử dụng CDI có thể thêm các trình gỡ rối mới vào hoạt động của CDT và hiển thị các kết quả gỡ rối trong viễn cảnh gỡ rối Eclipse/CDT. Bài viết này trình bày CDI một cách chi tiết. Phần 2 của loạt bài "Giao diện với trình gỡ rối CDT" (Interfacing with the CDT debugger) cho thấy cách CDI được xác định cho giao diện với GNU Debugger (gdb) thông qua Giao diện máy của nó (MI).

Ví dụ về trình cắm thêm CDI

Cách tốt nhất để tìm hiểu cách trình gỡ rối CDT làm việc là xem xét và thử nghiệm với mã thực. Bài viết này giải thích cách xây dựng một trình cắm thêm ít tính năng mở rộng CDT để cung cấp khả năng gỡ rối cơ bản. Không có trình gỡ rối thực nào có thể chạy được, nhưng bạn có thể sử dụng mã này như cơ sở để bổ sung thêm một trình gỡ rối riêng của bạn vào CDT.

Trình cắm thêm ví dụ này bao gồm ba phần mở rộng cho CDT và Eclipse Debug Framework (Khung công tác gỡ rối Eclipse):

org.eclipse.debug.core.launchConfigurationTypes
Tạo ra một trình khởi chạy riêng biệt để gỡ rối các ứng dụng C/C++.
org.eclipse.debug.ui.launchConfigurationTabGroups
Nhận gỡ rối các tham số cấu hình từ người dùng.
org.eclipse.cdt.debug.core.CDebugger
Tạo ra phiên làm việc gỡ rối cho ứng dụng C/C++ đã khởi chạy.

Bài viết này giải thích từng phần và đưa ra các mã ví dụ cho biết cách chúng làm việc. Sau đó, nó tìm hiểu sâu vào các hoạt động của CDI, bao gồm mô hình hóa CDI và khám phá cách CDI tạo ra các điểm ngắt và các điểm giám sát có thể.


Tạo một kiểu cấu hình khởi chạy tùy chỉnh

Trong Eclipse, quá trình bắt đầu một ứng dụng được gọi là khởi chạy ứng dụng theo chế độ Chạy (Run). Một phiên làm việc gỡ rối được tham chiếu như khởi chạy theo chế độ Gỡ rối (Debug). Một khi ở chế độ khởi chạy, bước tiếp theo là chọn kiểu cấu hình khởi chạy. Việc cho Eclipse biết chính xác cách ứng dụng nên được thực hiện hoặc được gỡ rối. Ví dụ, một kiểu cấu hình khởi chạy cho chế độ Debug định nghĩa trình gỡ rối có thể chạy được, các tùy chọn gỡ rối mặc định và cách thể hiện kết quả gỡ rối trong Eclipse. Hình 1 cho thấy các kiểu cấu hình được trình bày trong cửa sổ Cấu hình gỡ rối CDT (CDT Debug Configurations).

Hình 1. Cửa sổ Cấu hình khởi chạy CDT
Cửa sổ Cấu hình khởi chạy CDT

Để vào giao diện của trình gỡ rối mới với Eclipse, bước đầu tiên là tạo ra một kiểu cấu hình khởi chạy mới. Việc này đòi hỏi một trình cắm thêm để mở rộng điểm mở rộng org.eclipse.debug.core.launchConfigurationTypes. Trong Hình 1, bạn có thể thấy Kiểu cấu hình ví dụ ở bên trái cửa sổ. Liệt kê 1 trình bày các phần mở rộng mà nó định nghĩa kiểu mới này.

Liệt kê 1. Ví dụ về phần mở rộng LaunchConfigurationType
   <extension
         point="org.eclipse.debug.core.launchConfigurationTypes">
      <launchConfigurationType
            name="Example Configuration Type"
            delegate="org.dworks.debugexample.ExampleConfigurationDelegate"
            modes="debug"
            public="true"
            sourceLocatorId="org.eclipse.cdt.debug.core.sourceLocator"
            sourcePathComputerId="org.eclipse.cdt.debug.core.sourcePathComputer"
            id="org.dworks.debug.example.ExampleLaunch">
      </launchConfigurationType>
   </extension>

Kiểu cấu hình ví dụ được đặt trong cửa sổ Debug do trường các chế độ (modes) được thiết lập ở chế độ gỡ rối (debug) và các trường công khai (public) được đặt là đúng (true). Trường quan trọng nhất là đại diện (delegate), xác định lớp mà nó quản lý quá trình khởi chạy. Lớp này phải triển khai thực hiện giao diện ILaunchConfigurationDelegate và mã hóa một trường đại biểu từ đầu có thể phức tạp. Rất may, CDT làm cho cuộc sống của bạn dễ dàng hơn bằng cách cung cấp bốn lớp đại diện dựng sẵn:

LocalRunLaunchDelegate
Chạy một ứng dụng C/C++ tại chỗ.
LocalAttachLaunchDelegate
Gỡ rối ứng dụng tại chỗ.
CoreFileLaunchDelegate
Phân tích các tệp lõi sau khi thực hiện.
LocalCDILaunchDelegate
Vào giao diện của trình gỡ rối CDI tại chỗ.

CDT sử dụng ba lớp đầu tiên để khởi chạy phần mềm cục bộ. Người ta tạo ra lớp đại diện cuối cùng cho các công cụ bên ngoài CDT và dùng nó như trọng tâm thảo luận. Khi LocalCDILaunchDelegate được gọi để khởi chạy ở chế độ gỡ rối, nó nhận được thông tin về khai thác đã được gỡ rối và gửi các tham số tới trình gỡ rối này. Thông tin này phải được đóng gói trong đối tượng ILaunchConfiguration và cần một giao diện người dùng (UI) thích hợp để nhận được thông tin từ người sử dụng.


Thêm một nhóm tab vào cấu hình khởi chạy

Trước khi gỡ rối một ứng dụng Eclipse, bạn phải lựa chọn một kiểu cấu hình khởi chạy và, bằng cách nhấn đúp hoặc nhấn chuột phải, tạo một cấu hình khởi chạy mới. Tại thời điểm này, một bảng đồ họa xuất hiện ở bên phải và yêu cầu thông tin liên quan đến việc khởi chạy. Bảng này được gọi là một nhóm tab cấu hình khởi chạy; một ví dụ về nhóm tab được hiển thị ở bên phải Hình 1. Nhóm tab này bao gồm nhiều tab và tab có thể thấy trong Hình 1 chấp nhận các tên của khai thác được gỡ rối và và dự án của nó.

Mỗi lần khởi chạy phải có nhóm tab riêng của mình và điều này phải được xác định trong plug-in.xml như là một phần mở rộng của org.eclipse.debug.ui.launchConfigurationTabGroups. Liệt kê 2 cho thấy phần mở rộng này trông giống như trong ví dụ này.

Liệt kê 2. Khai báo nhóm tab ví dụ
   <extension
         point="org.eclipse.debug.ui.launchConfigurationTabGroups">
      <launchConfigurationTabGroup
            type="org.dworks.debug.example.ExampleLaunch"
            class="org.dworks.debug.example.ExampleTabGroup"
            id="org.dworks.debug.example.ExampleTabGroup">
      </launchConfigurationTabGroup>
   </extension>

Phần mở rộng này dễ hiểu. Trường kiểu (type) xác định cấu hình khởi chạy, lớp (class) xác định lớp tạo ra nhóm tab đó và id cho nhóm tab đó một tên duy nhất. Lớp do lớp (class) xác định phải triển khai thực hiện giao diện ILaunchConfigurationTabGroup và công việc của nó là tạo ra một hoặc nhiều ILaunchConfigurationTab. Các tab này cung cấp cho trình gỡ rối các thông tin cần để thao tác, gồm các cờ gỡ rối, vị trí của mã nguồn, và các địa chỉ bộ nhớ. Để đặt cấu hình một ILaunchConfigurationTab thực hiện công việc của nó, bạn cần triển khai hai phương thức quan trọng:

  • createControl(Composite parent)— Tạo ra giao diện người dùng cho tab trình gỡ rối (debugger).
  • performApply(ILaunchConfigurationWorkingCopy configuration)— Đặt cấu hình các tham số của trình gỡ rối.

Phương thức thứ hai đặc biệt quan trọng. Nó có chức năng gán các giá trị cho các thuộc tính trong LaunchConfigurationWorkingCopy. Đối tượng dữ liệu này nắm các thông tin cần cho gỡ rối, và nó được gửi đến trình gỡ rối khi bắt đầu phiên gỡ rối. Các tên thuộc tính được liệt kê trong giao diện ICDTLaunchConfigurationConstants. Các thuộc tính quan trọng bao gồm:

ATTR_DEBUGGER_ID
ID của trình gỡ rối được khởi chạy.
ATTR_DEBUGGER_SPECIFIC_ATTRS_MAP
Các thuộc tính cung cấp cho phiên làm việc của trình gỡ rối.
ATTR_PROJECT_NAME
Tên của dự án đang được gỡ rối.
ATTR_PROGRAM_NAME
Tên của chương trình đang được gỡ rối.
ATTR_PROGRAM_ARGUMENTS
Các tham số được cung cấp để chạy chương trình.
ATTR_PLATFORM
Hệ điều hành đang chạy chương trình.

ExampleConfigurationTabGroup tạo ra một tab cấu hình đơn: ExampleTab. Tab này hiển thị sáu hộp văn bản được khởi tạo sẵn — một cho mỗi thuộc tính được mô tả chi tiết trước. Hình 2 cho thấy ExampleTab trông giống thế nào. Bình thường, bạn nên tạo nhiều tab với nhiều nút điều khiển. CDT cung cấp lớp CDebuggerTab để cấu hình một trình gỡ rối C/C++.

Hình 2. Ví dụ về tab cấu hình khởi chạy
Ví dụ về tab cấu hình khởi chạy

Mã trong ExampleTab khởi tạo các thuộc tính gỡ rối bằng phương thức setDefaults(). Phương thức này xác định trình gỡ rối ví dụ như các trình gỡ rối sử dụng và tìm kiếm nguồn tài nguyên CDT được chọn để tìm dự án và chương trình để gỡ rối. Việc thực hiện tìm kiếm này không yêu cầu mã mới vì ExampleConfigurationTab mở rộng lớp CLaunchConfigurationTab của CDT. Lớp trừu tượng này cung cấp hai phương thức quan trọng: getContext(), trả về một nguồn tài nguyên CDT và initializeCProject(), khởi tạo thuộc tính ATTR_PROJECT_NAME với tên của dự án đó.


Tạo trình gỡ rối tuỳ chỉnh

Khi bạn nhấn vào Debug, Eclipse tìm kiếm trình gỡ rối được thuộc tính ATTR_DEBUGGER_ID xác định. Trong dự án ví dụ, điều này được đặt tới org.dworks.debugexample.ExampleDebugger, tương ứng với trường id trong phần mở rộng của dự án ví dụ của org.eclipse.cdt.debug.core.CDebugger. Liệt kê 3 trình bày phần mở rộng đầy đủ.

Liệt kê 3. Điểm mở rộng của trình gỡ rối ví dụ
   <extension point="org.eclipse.cdt.debug.core.CDebugger">
      <debugger
            platform="*"
            name="Example Debugger"
            modes="run"
            cpu="*"
            class="org.dworks.debugexample.ExampleDebugger"
            id="org.dworks.debugexample.ExampleDebugger">
      </debugger>
   </extension>

Khi trình gỡ rối khởi động, Eclipse sẽ kiểm tra xem nền tảng (platform) của trình gỡ rối và các phần tử cpu có hỗ trợ môi trường xử lý không. Nếu có, nó sẽ kiểm tra để đảm bảo chắc chắn phần tử chế độ (mode) của trình gỡ rối hỗ trợ các chế độ khởi chạy hiện tại. Nếu qua tất cả các phần kiểm tra, Eclipse tìm thấy lớp được xác định trong trường class. Lớp này triển khai thực hiện giao diện ICDIDebugger2 và do đó phải đưa ra một việc thực hiện phương thức createSession().

Mục tiêu của createSession() là kiến thiết một đối tượng thực hiện giao diện ICDISession. Đối tượng này cho phép CDT truy cập trình gỡ rối khi nó hoạt động. Việc truy cập này được cung cấp bằng một số các phương thức và ba phương thức quan trọng nhất là:

  • getSessionProcess()— Trả về quá trình hoạt động của trình gỡ rối (debugger).
  • getTargets()— Trả về một mảng các mục tiêu được gỡ rối.
  • getEventManager()— Trả về một đối tượng người quản lý để thêm và loại bỏ những bộ nghe.

Phương thức đầu tiên trong các phương thức này khá đơn giản và trả về một đối tượng quá trình (Process) tương ứng với các trình gỡ rối thực hiện được. Hai phương thức tiếp theo, getTargets()getEventManager(), được yêu cầu nhiều hơn; phần còn lại của bài viết này sẽ giải thích chúng. Vì những phương thức này phụ thuộc vào trình gỡ rối trực khai và vì không có một trình cắm thêm ví dụ nào, nên lớp ExampleDebugger để những phương thức này rỗng.


Các đích, các sự kiện, và mô hình CDI

Khi CDT gọi phương thức getTargets(), phiên làm việc phải cung cấp một ICDITarget cho mỗi quá trình đang được gỡ rối. Công việc của ICDITarget là nhận các lệnh gỡ rối, dịch chúng cho trình gỡ rối và gửi chúng đến trình gỡ rối. Ví dụ, khi bạn nhấn vào nút Step hay Resume trên viễn cảnh gỡ rối CDT, CDT gọi phương thức stepOver() hoặc resume() của đích, tương ứng. CDI không yêu cầu bất kỳ điều gì, như cách giao diện đích của trình gỡ rối hoạt động, nhưng Phần 2 giải thích cách gdb tương tác với một đích bằng cách sử dụng giao thức MI.

Ngoài việc gửi các lệnh tới trình gỡ rối, ICDITarget cũng truyền kết quả đầu ra của trình gỡ rối tới bất kỳ đối tượng nào quan tâm đến việc thu nhận nó. Điều này được thực hiện thông qua các sự kiện CDI, đây là nơi mà ở đó phương thức getEventManager() của phiên làm việc đi tới. Khi có bất kỳ đối tượng CDI nào, như một RegisterView, muốn được thông báo về sự kiện của trình gỡ rối, nó gọi getEventManager() để truy cập ICDIEventManager của phiên làm việc. Sau đó, nó gọi phương thức addEventListener() của trình quản lý để tự thêm nó vào như là một bộ nghe. Khi trình gỡ rối tạo ra kết xuất, đích đó gọi đến trình quản lý để báo cho tất cả các bộ nghe của nó. Trình quản lý thực hiện điều này bằng cách gọi một phương thức handleDebugEvents() của mỗi bộ nghe, mà phương thức này cần được các ICDIEventListener triển khai thực hiện.

CDI cung cấp một bộ tiêu chuẩn các ICDIEvent. Mỗi đáp ứng của trình gỡ rối hoặc thay đổi trong môi trường gỡ rối phải được đóng gói thành một trong những phần sau đây:

ICDIBreakpointMovedEvent
Được thực hiện khi người dùng chọn một điểm ngắt mới.
ICDIChangedEvent
Được thực hiện khi một khía cạnh của đích, như là một biến, đăng ký hay một phần của bộ nhớ, thay đổi giá trị.
ICDICreationEvent/ICDIDestroyedEvent
Được thực hiện khi một đối tượng, như một điểm ngắt, được tạo ra hoặc loại bỏ.
ICDIExitedEvent
Được thực hiện khi chương trình kết thúc.
ICDIRestartedEvent/ICDIDisconnectedEvent
Được thực hiện khi đích khởi động lại hoặc ngắt kết nối.
ICDIExecutableReloadedEvent
Được thực hiện khi một chương trình được nạp lại, như sau xây dựng mới.
ICDISuspendedEvent/ICDIRestartedEvent/ICDIResumedEvent
Được thực hiện khi việc thực hiện bị dừng lại, khởi động lại, hoặc tiếp tục.

Phần 2 mô tả chi tiết cách kết quả đầu ra gdb được biến đổi thành các MIEvent và cách các MIEvent trở thành các ICDIEvent.

ICDIChangedEvent đặc biệt quan trọng bởi vì nó được thực hiện mỗi khi thay đổi đích. Mỗi khía cạnh thay đổi được của đích do một ICDIObject thể hiện, và các đối tượng này tạo nên phân cấp được gọi là mô hình CDI. Chúng bao gồm các ICDIRegister, các ICDIVariable, các ICDIMemoryBlock, các ICDIThread và nhiều cái nữa. ICDITarget là gốc và vùng chứa của các đối tượng mô hình CDI này. Phương thức duy nhất mà mỗi ICDIObject phải triển khai thực hiện là getTarget().


Các điểm ngắt CDI và các điểm giám sát

Hai trong số các cách quan trọng nhất về việc kiểm soát trình gỡ rối sử dụng các điểm ngắt và các điểm giám sát. Một điểm ngắt tạm dừng trình gỡ rối khi nó chạm một vị trí cụ thể hay khi đáp ứng một điều kiện. Một điểm giám sát tạm dừng trình gỡ rối khi một biến đặc biệt được đọc hay ghi lên. CDI cung cấp hai giao diện để mô tả các giao diện này: ICDIBreakpoint và giao diện con của nó, ICDIWatchpoint. ICDIBreakpoint có thể là thường xuyên, tạm thời, hoặc được trợ giúp của phần cứng; còn ICDIWatchpoint có thể theo kiểu đọc, kiểu viết hay cả hai.

Bạn tạo và xóa các điểm ngắt và các điểm giám sát thông qua một cá thể của ICDIBreakpointManagement. Cá thể này biểu thị nhiều phương thức liên quan, chẳng hạn như setLineBreakpoint(), deleteBreakpoints()setWatchpoint(). Nhưng điều quan trọng cần nhớ rằng lớp trình cắm thêm của trình cắm thêm org.eclipse.debug.core cung cấp trình quản lý điểm ngắt riêng của nó, IBreakpointManager, mà bạn có thể truy cập nó bằng DebugPlugin.getDefault().getBreakpointManager(). Trong nhiều trường hợp, bạn có thể cần truy cập nó và chuyển đổi các IBreakpoint thành các ICDIBreakpoints.


Kết luận

Việc xây dựng một môi trường gỡ rối đồ họa từ đầu là một nhiệm vụ hết sức nặng nề (nhiệm vụ Héc quyn): không chỉ làm cho bạn phải biết bộ xử lý đích bên trong và bên ngoài, mà còn phải liên lạc với trình gỡ rối và chuyển kết quả đầu ra của nó cho một môi trường người dùng đồ họa. Đối với những người không là Héc quyn, Eclipse cung cấp một API để thêm các trình gỡ rối tùy chỉnh cho khung công tác CDT. API này được gọi là C/C++ Debugger Interface (CDI – Giao diện của trình gỡ rối C/C++) và bài viết này đã trình bày các cơ sở về các phép toán và cách sử dụng. Phần 2 của loạt bài này "Giao diện với trình gỡ rối CDT" giải thích cách CDT sử dụng CDI để giao diện với trình gỡ rối mã nguồn mở tốt nhất trong tất cả chúng: là GNU Debugger (gdb).


Tải về

Mô tảTênKích thước
Sample codeos-eclipse-cdt-debug-ex-debugger-plugin.zip15KB

Tài nguyên

Học tập

Lấy sản phẩm và công nghệ

Thảo luận

  • Các nhóm tin nền tảng Eclipse là bước đầu tiên của bạn để thảo luận các câu hỏi về Eclipse. (Chọn phần này sẽ khởi chạy ứng dụng đọc tin Usenet mặc định của bạn và mở eclipse.platform).
  • Các nhóm tin Eclipse có nhiều nguồn tài nguyên cho những người đã quan tâm đến việc sử dụng và mở rộng Eclipse.
  • Tham gia vào các blog developerWorks và và dành hết tâm trí cho cộng đồng developerWorks.

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Nguồn mở
ArticleID=438501
ArticleTitle=Giao diện với trình gỡ rối CDT, Phần 1: Hiểu về giao diện trình gỡ rối C/C++
publish-date=10232009