9

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

Marty
  • 5,926
  • 9
  • 53
  • 91

1 Answers1

16

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
Dave Lee
  • 6,299
  • 1
  • 36
  • 36
  • This is limited to the console though, right? That's a good start, but I wonder if there's a way to do it as part of the Xcode UI? I don't know anything about Xcode Extensions, but was wondering if that would have the capability. – Marty Jul 13 '20 at 17:44
  • 1
    The API lldb uses to tell Xcode about frames reports all the extant frames. Xcode already elides the frames (except for entry & leave) into system libraries. If you want that filtering to be more configurable, you should file an enhancement request with Xcode. – Jim Ingham Jul 13 '20 at 18:01