Продолжение моего предыдущего рассказа о перезаписи свойств только для чтения с помощью LLDB. На этот раз мы создадим точку останова, которая останавливается, когда стек вызовов соответствует строгому порядку и строгому подмножеству.
Stackpoint.py ‹- GIST!
import lldb """ NOTE: LLDB print adds a bunch of other stuff to the string like in this example (lldb) script -- ENTERED PYTHON REPL >>> print lldb.frame.GetDisplayFunctionName() ::-[ASLayoutTransition applySubnodeInsertionsAndMoves]() """ """ REMEMBER: This is the FILO callstack. It should match the same visual order that you see in the debugger. """ matchingCallStack = [ '-[_ASDisplayLayer layoutSublayers]' ] initializedParams = False def stop_matching_stack(debugger, command, result, internal_dict): global matchingCallStack global initializedParams if not initializedParams: debugger.HandleCommand('expr BOOL $matchedStack = 0x0') target = debugger.GetSelectedTarget() process = target.GetProcess() thread = process.GetSelectedThread() callStack = [frame.GetFunctionName() for frame in thread.frames] print callStack matchingCallStackCopy = list(matchingCallStack) matchedSignatures = 0 for signature in callStack: found = False for requirement in matchingCallStackCopy: if requirement in signature: found = True break if found: matchingCallStackCopy = matchingCallStackCopy[1:] matchedSignatures = matchedSignatures + 1 if matchedSignatures == len(matchingCallStack): print "Stack Matcher: Matched Stack!" debugger.HandleCommand('expr $matchedStack = 0x1') return True elif matchedSignatures > 0: print "Stack Matcher: Partial Match: " + str(matchedSignatures) print process debugger.HandleCommand('expr $matchedStack = 0x0') def __lldb_init_module(debugger, internal_dict): debugger.HandleCommand('command script add -f stackpoint.stop_matching_stack stop_matching_stack') print 'The "stop_matching_stack" python command has been installed and is ready for use.'
Например, есть высокочастотная часть кода, такая как общий метод суперкласса, как в моем примере, использующий невероятно быструю текстуру фреймворка пользовательского интерфейса. В этом случае есть функция -(void)applySubnodeInsertionAndMoves
, которая находится в конце нескольких разных точек вызова. Если бы мы попытались установить точку останова здесь, она бы пострадала от всех этих различных историй, и мы потеряли бы точность отладки конечного автомата, особенно в многопоточной ситуации.
Представляем Stackpoint.py, простой сценарий, в котором вы можете определить произвольный порядок элементов в стеке вызовов и, немного поработав вручную, остановить отладчик на соответствующем стеке. Алгоритм довольно прост, если у вас есть предложения по оптимизации, пишите в Gist!
ПРИМЕР
Перед запуском давайте загрузим скрипт при запуске сеанса LLDB, изменив ~/.lldbinit
. Вставьте следующее в любую строку отдельной строки, где ~/stackpoint.py
— это расположение stackpoint.py!
command script import ~/stackpoint.py
Итак, вот наша функция:
Очень простой. Мы хотим остановиться на строке 97, но только в том случае, если это было вызвано вызовом [CALayer layoutSublayers]
, а не вызовом [_ASDisplayLayer layoutSublayers]
.
Давайте перейдем к нашему скрипту stackpoint.py и поместим элемент в наш
matchingCallStack = [ '-[CALayer layoutSublayers]', 'earlier something in the call stack', 'main' ]
Алгоритм будет проходить и пытаться строго упорядочить, строгое подмножество соответствует сопоставлениюCallStack и стеку вызовов из текущего потока. Важно отметить, что этот сценарий записывает в глобальную переменную LLDB с именем $matchedStack
, которую можно использовать для условных точек останова.
Итак, затем мы устанавливаем точку останова для запуска функции stackmatch и дочернюю точку останова, которая имеет чтение условия из $matchedStack
, чтобы остановить выполнение. К вашему сведению, на снимке экрана показана символическая точка останова.
stop_matching_stack
Важно отметить, что родительская точка останова проверяет «Автоматически продолжить…», чтобы мы могли пропустить точку останова, если дочерняя точка останова не срабатывает.
И тогда мы можем настроить дочернюю точку останова.
И вуаля! Мы matcha да стек! 🥞
Каждый раз, когда вы хотите изменить стек, потому что, скажем, у вас что-то не так, вы всегда можете изменить список в самом скрипте, а затем запустить command source ~/.lldbinit
из сеанса LLDB в XCode, чтобы повторно инициализировать команду lldb stop_matching_stack
из скрипта python!