Trình gỡ lỗi

Gỡ lỗi dự án bằng Trình gỡ lỗi Visual Studio (LLDB) khi sử dụng Android Game Development Extension.

Chạy trình gỡ lỗi

Trước khi chạy được trình gỡ lỗi, bạn phải có thể xây dựng, triển khai và chạy trò chơi của mình trên Android. Hãy xem mục Chạy mẫu để biết thông tin chi tiết.

Sau khi chắc chắn rằng bạn có thể chạy trò chơi mà không cần trình gỡ lỗi, bạn có thể sử dụng trình gỡ lỗi bằng cách nhấn F5 hoặc chọn mục Start Debugging (Bắt đầu gỡ lỗi) trong trình đơn Debug (Gỡ lỗi). Bạn sẽ thấy một hộp thoại trong khi trình gỡ lỗi đang gắn vào trò chơi.

Việc khởi chạy trình gỡ lỗi sẽ mất từ 10 giây đến 1 phút hoặc lâu hơn, tuỳ thuộc vào dung lượng ứng dụng của bạn và số lượng biểu tượng cần tải khi khởi động. Việc gắn trình gỡ lỗi vào thiết bị mới lần đầu sẽ mất nhiều thời gian hơn vì trình gỡ lỗi phải tải một số thư viện Android từ thiết bị xuống máy chủ. Nếu những lần đầu thử với thiết bị mới kéo dài hơn 1 phút, hãy cân nhắc huỷ phiên gỡ lỗi rồi bắt đầu lại.

Khi bạn chạy trình gỡ lỗi theo cách này, trò chơi sẽ khởi động ở chế độ Đang chờ trình gỡ lỗi và không thực thi bất kỳ mã nào nằm bên trong cho đến khi trình gỡ lỗi kết nối. Việc này cũng cho phép bạn gỡ lỗi phần khởi động của trò chơi.

Bạn có thể đọc Tài liệu về Visual Studio để tìm hiểu thêm thông tin về những tính năng cụ thể của trình gỡ lỗi Visual Studio.

Gắn vào một quy trình

Nếu muốn gỡ lỗi một trò chơi đang chạy trên thiết bị thực hoặc thiết bị ảo, bạn có thể gắn trình gỡ lỗi vào quy trình này từ Visual Studio.

Hãy đảm bảo đang mở một giải pháp Android trong Visual Studio và:

  1. Chuyển đến trình đơn Debug (Gỡ lỗi) rồi chọn Attach to Process... (Gắn vào quy trình…).
  2. Trong trình đơn thả xuống Transport (Truyền tải), hãy chọn Android Game Development Extension.
  3. Trong trình đơn thả xuống Qualifier (Bộ hạn định), hãy chọn thiết bị Android của bạn.
  4. Chọn quy trình trò chơi từ danh sách các quy trình có sẵn rồi nhấp vào Attach (Gắn).

Gắn vào quy trình

Thực thi các lệnh LLDB.Shell

Khi phiên gỡ lỗi đang hoạt động, hãy sử dụng Cửa sổ Command (Lệnh) của Visual Studio để chạy lệnh LLDB.Shell.

Định dạng lệnh:

LLDB.Shell [command]

Ví dụ:

>LLDB.Shell expr myIntVariable = 9
Status:  Success
Output Message:
(int) $2 = 9

Trực quan hoá dữ liệu

Thông số định dạng

Bạn có thể thay đổi định dạng hiển thị của một giá trị trong các cửa sổ Autos (Tự động), Locals (Cục bộ), Watch (Xem) và cửa sổ biến DataTip (Mẹo dữ liệu) bằng cách sử dụng các thông số định dạng.

Bạn có thể tìm thấy thông số định dạng ở cuối biểu thức. Thông số định dạng bắt đầu bằng dấu phẩy, theo sau là một chuỗi ngắn. Ví dụ: thông số ,x trong biểu thức _myInt,x sẽ định dạng myInt là số thập lục phân viết thường.

Bạn có thể sử dụng trực tiếp thông số định dạng trong cửa sổ Watch (Xem) hoặc trong Autos (Tự động), Locals (Cục bộ) và cửa sổ biến DataTip (Mẹo dữ liệu) bằng cách thêm thông số định dạng vào biểu thức Natvis. Hãy xem Natvis để biết thêm thông tin.

Danh sách thông số hỗ trợ

Tên định dạng Thông số Mô tả
boolean B hiển thị dưới dạng boolean đúng/sai (true/false), sử dụng quy tắc tuỳ chỉnh rằng 0 là sai và mọi giá trị khác là đúng
nhị phân b hiển thị ở dạng chuỗi bit
nhị phân, không có tiền tố 0b bb hiển thị dưới dạng một chuỗi bit không có tiền tố 0b
byte y hiển thị các byte, nhưng cũng tìm cách hiển thị dưới dạng ký tự ASCII
ví dụ: (int *) c.sp.x = 50 f8 bf 5f ff 7f 00 00 P.._....
byte bằng ASCII Y hiển thị các byte, nhưng cũng tìm cách hiển thị dưới dạng ký tự ASCII
ví dụ: (int *) c.sp.x = 50 f8 bf 5f ff 7f 00 00 P.._....
ký tự c hiển thị các byte dưới dạng ký tự ASCII
ví dụ: (int *) c.sp.x = P\xf8\xbf_\xff\x7f\0\0
ký tự in được C hiển thị các byte dưới dạng ký tự ASCII in được
ví dụ (int *) c.sp.x = P.._....
số phức dấu phẩy động F diễn giải giá trị này dưới dạng phần thực và ảo của số phức dấu phẩy động
ví dụ (int *) c.sp.x = 2.76658e+19 + 4.59163e-41i
thập phân d, i hiển thị dưới dạng số nguyên có dấu (đây không phải là ép kiểu mà chỉ là hiển thị các byte dưới dạng số nguyên có dấu)
liệt kê E,en hiển thị dưới dạng liệt kê, hiển thị tên của giá trị (nếu có) hoặc giá trị số nguyên
ví dụ: (enum enumType) val_type = eValue2
thập lục phân – chữ thường x, h hiển thị bằng ký hiệu thập lục phân viết thường (đây không phải là ép kiểu mà chỉ là hiển thị các byte dưới dạng thập lục phân)
thập lục phân – viết hoa X, H hiển thị bằng ký hiệu thập lục phân viết hoa (đây không phải là ép kiểu mà chỉ là hiển thị các byte dưới dạng thập lục phân)
thập lục phân – chữ thường, không có tiền tố 0x xb, hb hiển thị bằng ký hiệu thập lục phân viết thường mà không có tiền tố 0x (đây không phải là ép kiểu mà chỉ là hiển thị các byte dưới dạng thập lục phân)
thập lục phân – viết hoa, không có tiền tố 0x Xb, Hb hiển thị bằng ký hiệu thập lục phân viết hoa mà không có tiền tố 0x (đây không phải là ép kiểu mà chỉ là hiển thị các byte dưới dạng thập lục phân)
số thực dấu phẩy động f hiển thị dưới dạng số thực dấu phẩy động (đây không phải là ép kiểu mà chỉ là diễn giải các byte dưới dạng giá trị thực dấu phẩy động IEEE754)
bát phân o hiển thị bằng ký hiệu bát phân
Loại hệ điều hành O hiển thị dưới dạng MacOS OSType
ví dụ (float) x = '\n\x1f\xd7\n'
chuỗi – chuỗi C s hiển thị dưới dạng chuỗi C kết thúc bằng ký tự rỗng
ví dụ: "xin chao moi nguoi"
chuỗi – chuỗi C, không có dấu ngoặc kép sb hiển thị dưới dạng chuỗi C kết thúc bằng ký tự rỗng mà không có dấu ngoặc kép,
ví dụ: xin chao moi nguoi
chuỗi – UTF-8 s8 hiển thị dưới dạng chuỗi UTF-8 kết thúc bằng ký tự rỗng
ví dụ: u8"xin chao moi nguoi ☕"
chuỗi – UTF-8, không có dấu ngoặc kép s8b hiển thị dưới dạng chuỗi UTF-8 kết thúc bằng ký tự rỗng mà không có dấu ngoặc kép
ví dụ: xin chao moi nguoi ☕
chuỗi – UTF-16 su hiển thị dưới dạng chuỗi UTF-16 kết thúc bằng ký tự rỗng
Ví dụ: u"xin chao moi nguoi ☕"
chuỗi – UTF-16, không có dấu ngoặc kép sub hiển thị dưới dạng chuỗi UTF-16 kết thúc bằng ký tự rỗng mà không có dấu ngoặc kép
ví dụ: xin chao moi nguoi ☕
chuỗi – UTF-32 s32 hiển thị dưới dạng chuỗi UTF-32 kết thúc bằng ký tự rỗng
ví dụ: U"xin chao moi nguoi ☕"
chuỗi – UTF-32, không có dấu ngoặc kép s32b hiển thị dưới dạng chuỗi UTF-32 kết thúc bằng ký tự rỗng mà không có dấu ngoặc kép
ví dụ: xin chao moi nguoi ☕
unicode16 U hiển thị dưới dạng ký tự UTF-16
ví dụ: (float) x = 0xd70a 0x411f
unicode32 U32 hiển thị dưới dạng ký tự UTF-32
ví dụ (float) x = 0x411fd70a
số thập phân không dấu u hiển thị dưới dạng số nguyên không dấu (đây không phải là ép kiểu mà chỉ là hiển thị các byte dưới dạng số nguyên không dấu)
con trỏ p hiển thị dưới dạng con trỏ gốc (trừ khi đây thực sự là một con trỏ, địa chỉ thu được có thể không hợp lệ)
số nguyên phức I diễn giải giá trị này dưới dạng phần thực và ảo của số nguyên phức
ví dụ (int *) pointer = 1048960 + 1i
mảng ký tự a hiển thị dưới dạng mảng ký tự
ví dụ (char) *c.sp.z = {X}
Thô ! định dạng thô, bỏ qua mọi tuỳ chỉnh chế độ xem loại dữ liệu

Natvis

Khung Natvis cho phép bạn tuỳ chỉnh cách Visual Studio hiển thị các loại gốc trong cửa sổ biến của trình gỡ lỗi. Ví dụ: sử dụng Natvis để tuỳ chỉnh màn hình cho các cửa sổ Watch (Xem), Locals (Cục bộ) và Data Tips (Mẹo dữ liệu).

Tính năng Natvis được bật theo mặc định. Tuy nhiên, bạn có thể tắt tính năng này từ Visual Studio bằng cách đặt cờ Tools > Options > Android Game Development Extension > Natvis (Công cụ > Tuỳ chọn > Android Game Development Extension > Natvis) thành Disabled (Tắt).

Tải tệp Natvis

Visual Studio tải tệp Natvis từ ba vị trí được liệt kê dưới đây và tải lại các tệp đó mỗi khi bạn bắt đầu một phiên gỡ lỗi. Các tệp phải tuân thủ lược đồ Natvis của Visual Studio 2017.

  • Tệp .natvis thuộc một dự án đã tải hoặc mục giải pháp cấp cao nhất.
  • Thư mục dành riêng cho người dùng (%USERPROFILE%\Documents\Visual Studio 2017\Visualizers)
  • Thư mục trên toàn hệ thống (%VSINSTALLDIR%\Common7\Packages\Debugger\Visualizers)
Tải lại tệp Natvis

Tải lại tệp Natvis trong một phiên gỡ lỗi bằng cách đánh giá .natvisreload trong Cửa sổ Command (Lệnh) hoặc cửa sổ Watch (Xem).

Tệp Natvis mẫu

Tệp Natvis mẫu này bao gồm tất cả thẻ và thuộc tính hiện được hỗ trợ.

<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">

  <Type Name="demo::Vector&lt;*&gt;">
    <AlternativeType Name="MySimilarVectorType&lt;*&gt;"/>

    <!-- Included to show the <SmartPointer> feature is supported. -->
    <SmartPointer Optional="true" Usage="Minimal">ptr</SmartPointer>

    <!-- Included to show the <DisplayString> feature is supported. -->
    <DisplayString Condition="_size == 0" Optional="true">()</DisplayString>
    <DisplayString Condition="_size == 1">(x={_items[0]})</DisplayString>
    <DisplayString Condition="_size == 2">(x={_items[0]}, y={_items[1]})</DisplayString>
    <DisplayString Condition="_size == 3">(x={_items[0]}, y={_items[1]}, z={_items[2]})</DisplayString>
    <DisplayString>[Size={_size,x}] (x={_items[0]}, y={_items[1]}, z={_items[2]}, ...)</DisplayString>

    <!-- Included to show the <StringView> feature is supported. -->
    <StringView Condition="true" Optional="true">_stringViewText</StringView>

    <Expand HideRawView="false">
      <!-- Included to show the <Item> feature is supported. -->
      <Item Name="X" Condition="_size &lt; 4 &amp;&amp; _size &gt;= 1" Optional="true">_items[0]</Item>
      <Item Name="Y" Condition="_size &lt; 4 &amp;&amp; _size &gt;= 2" Optional="true">_items[1]</Item>
      <Item Name="Z" Condition="_size &lt; 4 &amp;&amp; _size &gt;= 3" Optional="true">_items[2]</Item>

      <!-- Included to show the <ArrayItems> feature is supported. -->
      <ArrayItems Condition="_size >= 4" Optional="true">
        <Size Condition="true" Optional="true">_size</Size>
        <ValuePointer Condition="true">_items</ValuePointer>
      </ArrayItems>

      <!-- Included to show the <IndexListItems> feature is supported. -->
      <IndexListItems Condition="true" Optional="true">
        <Size Condition="true" Optional="true">_listSize</Size>
        <ValueNode Condition="true">_list[%i]</ValueNode>
      </IndexListItems>

      <!-- Included to show the <LinkedListItems> feature is supported. -->
      <LinkedListItems Condition="true" Optional="true">
        <Size Optional="true">_listSize</Size>
        <HeadPointer>_head</HeadPointer>
        <NextPointer>_next</NextPointer>
        <ValueNode>_value</ValueNode>
      </LinkedListItems>

      <!-- Included to show the <ExpandedItem> feature is supported. -->
      <ExpandedItem Condition="true" Optional="true">_childVar</ExpandedItem>

      <!-- Included to show the <Synthetic> feature is supported. -->
      <Synthetic Name="[Size]" Condition="true" Optional="true">
        <DisplayString>_size</DisplayString>
        <Expand HideRawView="true">
          <!-- Any supported <Expand> sub-tags. -->
        </Expand>
      </Synthetic>

      <!-- Included to show the <TreeItems> feature is supported. -->
      <TreeItems Condition="true" Optional="true">
        <Size>_treeSize</Size>
        <HeadPointer>_head</HeadPointer>
        <LeftPointer>_left</LeftPointer>
        <RightPointer>_right</RightPointer>
        <ValueNode>_value</ValueNode>
      </TreeItems>

      <!-- Included to show format specifiers are supported. -->
      <Item Name="[Hex Dump at {_index,x}]">myInt[_index],x</Item>
    </Expand>
  </Type>
</AutoVisualizer>

Tạo tệp Natvis

Visual Studio hỗ trợ việc tạo các tệp Natvis của riêng bạn. Để biết thêm thông tin về cách tuỳ chỉnh các cửa sổ biến của trình gỡ lỗi, hãy xem MSDN {: liên kết ngoài}.

Gỡ lỗi tệp Natvis

Trong một số trường hợp, lỗi sẽ được trình bày dưới dạng Giá trị của biến (ví dụ: trong cửa sổ Auto (Tự động), Watch (Xem), v.v.). Ví dụ: <error: use of undeclared identifier 'missingVar'>

Bạn có thể xem thêm thông tin chi tiết về lỗi bằng cách mở tệp GoogleAndroid.log trên thanh công cụ Android Game Development Extension.

Các hạn chế đã biết

  • Nếu thẻ hoặc thuộc tính không được liệt kê trong tệp ví dụ ở trên, thì thẻ hoặc thuộc tính đó hiện không được hỗ trợ. Visual Studio bỏ qua các thẻ và thuộc tính không được hỗ trợ, vì vậy, bạn có thể để các thẻ và tệp này trong tệp Natvis hiện có và tệp sẽ hoạt động, miễn là tệp sử dụng lược đồ của chúng tôi.

  • Mặc dù được lược đồ yêu cầu, nhưng thuộc tính Usage không được hỗ trợ cho <SmartPointer>. Tuy nhiên, LLDB không hạn chế quyền truy cập các toán tử được xác định trong C++, để có thể xác định mọi toán tử bắt buộc trong C++.