Advanced Debug Part 1

Không có chương trình nào là không có lỗi, thế nên là lập trình viên thì luôn luôn phải đối mặt với nó. Có những lỗi rất dễ dàng tìm ra bằng cách đọc code. Tuy nhiên có những lỗi rất khó tìm được ra nguyên nhân. Hoặc chúng ta mới vào dự án chưa hiểu được luồng của phần mềm thì cũng rất khó để tìm ra nguyên nhân nếu không có cách tiếp cận đúng.

Trong series chúng ta sẽ tìm hiểu về cách debug nâng cao để phần nào giúp các bạn có kiến thức và hướng tiếp cận tốt hơn để tìm ra những lỗi mà khó tìm. Hoặc giúp chúng ta dễ dàng debug bug những dự án chúng ta mới vào được tốt hơn.

Trong bài này mình sử dụng trình debug mặc định của Xcode là lldb.

Kiến thức cơ bản

Khi debug chúng ta có thể dùng 2 lệnh lldb là ppo. 2 lệnh này đều dùng để in ra biến mà chúng ta muốn xem giá trị. ppo khác nhau ở chỗ là po thường sẽ in ra chi tiết hơn so với p (objc), và p thường dùng để in ra biến là số.

Frame variable

Dùng lệnh frame variable sẽ giúp chúng ta in ra các biến nằm trên stack frame hiện tại. Stack frame là một vùng nhớ được tạo ra để lưu trữ các biến cục bộ, và lưu trữ một số thanh ghi cần thiết trong quá trình gọi hàm. Stack frame được giới hạn từ địa chỉ ở thanh ghi rbp tới thanh ghi rsp ( kiến trúc x86_64).

Các bạn có thể thấy kết quả output ra có biến self, _cmd, url và request tương ứng với code. Nhiều lúc Xcode không show hết các local var thì lệnh này rất hữu ích để chúng ta xem giá trị các biến local.

Thay đổi giá trị biến

Giả sử mình có một đoạn check điều kiện load data như sau.

- (void)loadDataIfNeed {
  NSDate *date = [NSDate date];
  BOOL needLoadData = NO;
  NSInteger minute = [NSCalendar.currentCalendar component:NSCalendarUnitMinute fromDate:date];
  if (minute == 0) {
    needLoadData = YES;
  }

  if (needLoadData) {
    [self loadData];
  }
}

- (void)loadData {
  NSLog(@"handle load data");
}

Trong function loadDataIfNeed sẽ check nếu thời gian hiện tại có số phút là 0 thì mới reloadData. Tuy nhiên bạn đang cần test phần load dữ liệu xem có chạy đúng hay không. Không nhẽ lại phải sửa lại logic check đó để test.

Khi này chúng ta sẽ đặt breakpoint vào dòng 5. Và sử dùng lệnh lldb để thay đổi giá trị của biến minute.

expression minute = 0

Bây giờ chạy tiếp thì biến minute của chúng ta đã có giá trị là 0. Và chương trình sẽ gọi vào hàm loadData và in ra text “handle load data” rồi.

Ngoài mục đích đổi tên biến, chúng ta có thể dùng nó để gọi hàm hay thực hiện một số đoạn code khác.

Breakpoint

Nhiều người tưởng rằng khi debug chỉ cần đặt breakpoint và xem giá trị của biến. Tuy nhiên đặt breakpoint cũng là một nghệ thuật, việc sử dụng breakpoint thành thạo sẽ giúp bạn debug dễ hơn.

Điều kiện của breakpoint

Breakpoint cho phép bạn thêm điều kiện cho nó. Nếu điều kiện thoả mãi breakpoint sẽ dừng. Giả sử bạn có đoạn code sau

- (void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];

  [self loadDataIfNeed];
  [self loadDataFrom:@"https://thanhvu.dev/admin"];
  [self loadDataFrom:@"https://thanhvu.dev/about"];
  [self loadDataFrom:@"https://thanhvu.dev"];
}

- (void)loadDataFrom:(NSString *)url {
  NSLog(@"URL %@", url);
}

Trong ví dụ trên, gọi load data từ rất nhiều url. Bạn chỉ muốn debug trường hợp load data từ https://thanhvu.dev thôi chẳng hạn. Thì bạn đặt breakpoint vào method loadDataFrom và set điều kiện cho nó.

Sử dụng điều kiện breakpoint sẽ giúp bạn dừng đúng chỗ mong muốn. Tránh tình trạng phải bấm debug continue nhiều lần mới tới chỗ mình mong muốn.

Action của breakpoint

Breakpoint còn cho phép chúng ta thực hiện action khi breakpoint được dừng lại. Quay trở lại ví dụ ở phần “thay đổi giá trị biến” bên trên. Thay vì lần nào test cũng gõ lệnh để thay đổi biến minute thì chúng ta có thể tạo breakpoint thực hiện lệnh thay đổi giá trị biến minute

Không chỉ một, mà breakpoint cho phép ta thêm nhiều action.

Breakpoint không dừng lại

Vẫn là ví dụ bên trên, Tuy nhiên chúng ta không muốn breakpoint dừng ở đó nữa mà cứ chạy thẳng và thực hiện thay đổi biến minute thôi. Thì Xcode cho phép ta chạy thẳng qua breakpoint đó bằng cách tick vào option Automatically continue after evaluating actions.

Lúc này Breakpoint cứ chạy thẳng tuột và thực hiện action mà chúng ta đã define.

Như vậy phần 1 đến đây thôi, các bạn đón đọc tiếp phần 2 của series này nhé.

Leave a Reply