Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to hide the third-party calls in the call stack in Xcode

When I work with Realm or RxSwift and there's an exception, I get these giant call stacks that I don't care about, and have to find the one or two lines that are my code. I'd like to only show symbols on the call stack that are from within the target running, not linked libraries. How can I hide these?

enter image description here

like image 310
Marty Avatar asked Jul 10 '20 01:07

Marty


People also ask

Is call stack and stack same?

In computer science, a call stack is a stack data structure that stores information about the active subroutines of a computer program. This kind of stack is also known as an execution stack, program stack, control stack, run-time stack, or machine stack, and is often shortened to just "the stack".

How do you represent a call stack?

In the Call Stack window, open the shortcut menu. Choose Show Call Stack on Code Map (Ctrl + Shift + `).

What is the purpose of the call stack?

A call stack is a mechanism for an interpreter (like the JavaScript interpreter in a web browser) to keep track of its place in a script that calls multiple functions — what function is currently being run and what functions are called from within that function, etc.


1 Answers

I don't know of a way to filter what Xcode displays, but you may find lldb's python scripting helpful. There are at least three possible ways to filter the stack:

  1. Frame name
  2. Library
  3. Source path

First, to filter the stack by name, a regex can be used to check the function name. The regex can either match the frames you want to remove, or what you want to keep. From the given stack trace, I can't tell which ones you want to keep so I'll demonstrate removing Realm frames:

(lldb) script
import re
for frame in lldb.thread:
    if not re.search("[Rr]ealm|RLM", frame.name):
        print(f"{frame.idx}: {frame.name}")

Matching by function name isn't always ideal, it could be a long or complicated regex. If the libraries you want to filter happen to be dynamic libraries or frameworks, then the easiest method is to filter by "module":

(lldb) script
for frame in lldb.thread:
    if frame.module.file.basename != "Realm":
        print(f"{frame.idx}: {frame.name}")

The third option is to filter by the source path associated with each frame. I don't know this would work for Realm (it depends on the debug info), but I have done this with RxSwift. In this example, frames that have RxSwift/ in their source path will not be printed:

(lldb) script
for frame in lldb.thread:
    if "RxSwift/" not in frame.line_entry.file.fullpath:
        print(f"{frame.idx}: {frame.name}")

Reusable Command

These methods can be combined to make a stack trace command that's suited for your project. To make that command, put your custom for loop into a file, and load it from your ~/.lldbinit:

command script import path/to/mystack.py

When writing an lldb command, a couple changes need to be made. Here's an example that combines two of the above methods:

import lldb
import re

REALM_PATTERN = re.compile("[Rr]ealm|RLM")

@lldb.command()
def mystack(debugger, command, ctx, result, _):
    for frame in ctx.thread:
        if not is_rx(frame) and not is_realm(frame):
            print(f"{frame.idx}: {frame.name}", file=result)

def is_realm(frame):
   return REALM_PATTERN.search(frame.name)

def is_rx(frame):
   return "RxSwift/" in frame.line_entry.file.fullpath
like image 120
Dave Lee Avatar answered Oct 15 '22 12:10

Dave Lee