Как "вручную" символизировать [NSThread callStackSymbols] (получить начальный адрес для atos) (iOS)

Цель:

Я хочу символизировать "вывод" [NSThread callStackSymbols],

Примечания стороны:

Я знаю, как это сделать с помощью журналов аварий. Однако мне нужно исследовать некоторые проблемы, в которых я хотел бы взглянуть на стек вызовов. К сожалению, в наши дни все адреса фреймворка <redacted>, Вызывать сбой в нужных точках (или в конце - см. Конец моего вопроса) не совсем приемлемо, но если я не смогу найти другое решение, это будет путь.

Я должен запустить свои тесты на устройстве, поэтому я не могу использовать симулятор.

Текущий подход:

Когда я называю это:

NSLog(@"call stack:\n%@", [NSThread callStackSymbols]);

Я получаю этот вывод:

2015-12-08 15:04:03.888 Conversion[76776:4410388] call stack:
(
    0   Conversion                          0x000694b5 -[ViewController viewDidLoad] + 128
    1   UIKit                               0x27259f55 <redacted> + 1028
    ...
    9   UIKit                               0x274f67a7 <redacted> + 134
    10  FrontBoardServices                  0x2b358ca5 <redacted> + 232
    11  FrontBoardServices                  0x2b358f91 <redacted> + 44
    12  CoreFoundation                      0x230e87c7 <redacted> + 14
    ...
    16  CoreFoundation                      0x23038ecd CFRunLoopRunInMode + 108
    17  UIKit                               0x272c7607 <redacted> + 526
    18  UIKit                               0x272c22dd UIApplicationMain + 144
    19  Conversion                          0x000767b5 main + 108
    20  libdyld.dylib                       0x34f34873 <redacted> + 2
)

("Конверсия" в этом выводе - приложение.)

Теперь я могу использовать эту команду для "символизации" адресов:

xcrun atos -o /path/to/Conversion.app -arch arm64 -l 0x0???

Запустите так (конечно, с правильным значением для -l), Я могу ввести такие адреса, как 0x000694b5 и это будет выплевывать что-то вроде -[ViewController viewDidLoad] + 128, Конечно, проблема заключается в начальном адресе (значение опции -l).

Когда у меня есть журнал аварий, я могу их получить. Тем не менее, я хотел бы уйти без сбоев.

Вопросы:

Q1: Могу ли я определить начальный адрес во время выполнения и, возможно, включить его в вывод журнала, чтобы я мог передать его atos -l вариант?

РЕДАКТИРОВАТЬ: похоже, что это возможно, как это: (спасибо NSProgrammer за ответ /questions/553813/atos-i-dwarfdump-ne-budut-simvolizirovat-moj-adres/553825#553825)

#import <mach-o/dyld.h>

...
intptr_t slide = _dyld_get_image_vmaddr_slide(0);
const struct mach_header * load_addr = _dyld_get_image_header(0);
NSLog(@"slide %lx load addr %lx", (long)slide, (long)load_addr);

/РЕДАКТИРОВАТЬ

(Поскольку меня интересуют вызовы метода платформы, мне, безусловно, нужны начальные адреса структур. Начальный адрес приложения часто меняется (рандомизация), я пока не знаю, рандомизированы ли начальные адреса платформы.)

Q2: есть ли другие подходы к исследованию методов в стеке вызовов? (Точки останова также довольно неуклюжи в моем сценарии.)

РЕДАКТИРОВАТЬ:

Q3: Как я могу символизировать адреса фреймворков? Например, где я могу найти dSYM (или что-то еще) для UIKit?

(Например, у меня есть что-то по адресу: ~/Library/Developer/Xcode/iOS\ DeviceSupport/9.1\ \(13B143\)/Symbols/System/Library/Frameworks/UIKit.framework/, Я рассмотрю более подробно здесь.)

/РЕДАКТИРОВАТЬ

Может быть решение:

Одним из способов может быть сохранение выходных данных журнала в файл, и в конце тестов вызовет сбой в приложении. Таким образом, в журнале сбоев будут отображаться начальные адреса, и с помощью информации стека вызовов из журналов я смогу символизировать callStackSymbols выход. Я попробую это дальше.

2 ответа

Добавление фрагментов журнала в журнал сбоев работало до сих пор, поэтому я добавлю это в качестве ответа на данный момент. (Лучшие ответы определенно приветствуются:-))

Подготовка:

Добавьте запись callStackSymbols в соответствующие места исходного кода:

NSLog(@"call stack:\n%@", [NSThread callStackSymbols]);

Выключите приложение (например, при открытии определенного экрана):

strcpy(0, "000");

Перенаправить вывод журнала в файл:

FILE *logfile = freopen([pathForLog cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);

Исполнение:

В Xcode запустите приложение один раз, чтобы оно было установлено на устройстве, а затем остановите его. Затем запустите приложение прямо на устройстве, чтобы XCode не уловил сбой.

Используйте приложение, чтобы стек вызовов регистрировался (может быть много раз).

В конце концов произойдет сбой приложения (в моем случае откройте определенный экран, который вызывает неверную команду strcpy).

Получение файлов:

В Xcode откройте "Окно -> Устройства", выберите устройство, выберите приложение и загрузите контейнер приложения, чтобы можно было извлечь файл журнала.

На том же экране откройте журналы устройства и экспортируйте журнал сбоев.

В Xcode откройте "Окно -> Проекты", выберите проект и сделайте так, чтобы он отображал папку производных данных в Finder (маленькая кнопка со стрелкой справа от пути к производным данным). В папке производных данных перейдите в "Build/Products/Debug-iphoneos/" и скопируйте MyApp.app и MyApp.app.dSYM.

Настройте журнал сбоя:

Для меня это работало, чтобы добавить дополнительные разделы потока прямо перед "Binary Images:". Так что найдите местоположение "Binary Images:". Вставьте строку "Тема 9999:". Скопируйте и вставьте дампы стека вызовов и удалите столбцы начальных пробелов, чтобы они выглядели так:

2015-12-09 15:28:58.971 MyApp[21376:3050653] call tree (
Thread 9999:
0   MyApp                               0x00000001001d95f8 -[MyClass myMethod:] + 100
1   UIKit                               0x000000018a5fc2ac <redacted> + 172
2   UIKit                               0x000000018a5d5ca4 <redacted> + 88
3   UIKit                               0x000000018a5d5b8c <redacted> + 460
4   UIKit                               0x000000018a5d5cc0 <redacted> + 116
5   UIKit                               0x000000018a5d5b8c <redacted> + 460
6   UIKit                               0x000000018a5d5cc0 <redacted> + 116
7   UIKit                               0x000000018a5d5b8c <redacted> + 460
8   UIKit                               0x000000018a8e85ac <redacted> + 460
9   UIKit                               0x000000018a5d4abc <redacted> + 96
10  UIKit                               0x000000018a935b7c <redacted> + 344
11  UIKit                               0x000000018a9306f8 <redacted> + 124
12  UIKit                               0x000000018aa584d8 <redacted> + 44
13  UIKit                               0x000000018a933d9c <redacted> + 188
14  UIKit                               0x000000018a70b668 <redacted> + 116
15  UIKit                               0x000000018a70b454 <redacted> + 252
16  UIKit                               0x000000018a70af38 <redacted> + 1404
17  UIKit                               0x000000018a70a9a8 <redacted> + 124
18  UIKit                               0x000000018a616d3c <redacted> + 312
19  UIKit                               0x000000018a616bc4 <redacted> + 108
20  QuartzCore                          0x0000000189dddc2c <redacted> + 284
21  libdispatch.dylib                   0x000000019a3a96a8 <redacted> + 16
22  libdispatch.dylib                   0x000000019a3aedb0 _dispatch_main_queue_callback_4CF + 1844
23  CoreFoundation                      0x00000001850001f8 <redacted> + 12
24  CoreFoundation                      0x0000000184ffe060 <redacted> + 1628
25  CoreFoundation                      0x0000000184f2cca0 CFRunLoopRunSpecific + 384
26  GraphicsServices                    0x000000018ff94088 GSEventRunModal + 180
27  UIKit                               0x000000018a644ffc UIApplicationMain + 204
28  MyApp                               0x0000000100093918 main + 124
29  libdyld.dylib                       0x000000019a3da8b8 <redacted> + 4

Binary Images:
...

Строка "Thread 9999:" создает symbolicatecrash Скрипт хочет символизировать следующие строки. Я выбрал 9999, так что я знаю, что это были мои добавленные разделы.

Запустить символику:

Найти symbolicatecrash сценарий:

$ find /Applications/Xcode.app -name symbolicatecrash -type f
/Applications/Xcode.app/Contents/SharedFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash
$ 

Команда символизации выглядит примерно так:

$ symbolicatecrash myapp.crash MyApp.app.dSYM > myapp-sym.crash

Вам нужно установить DEVELOPER_DIR и предварительно указать путь к скрипту, так что в конечном итоге это будет выглядеть так:

$ DEVELOPER_DIR='/Applications/Xcode.app/Contents/Developer' /Applications/Xcode.app/Contents/SharedFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash myapp.crash MyApp.app.dSYM > myapp-sym.crash

Или завернутый для лучшего просмотра:

$ DEVELOPER_DIR='/Applications/Xcode.app/Contents/Developer' ...
/Applications/Xcode.app/Contents/SharedFrameworks/ ...
DTDeviceKitBase.framework/Versions/A/Resources/ ...
symbolicatecrash myapp.crash MyApp.app.dSYM > myapp-sym.crash

Результат:

Символизированные фрагменты теперь выглядят так:

2015-12-09 15:28:58.971 MyApp[21376:3050653] call tree (
Thread 9999:
0   MyApp                               0x00000001001d95f8 -[MyClass myMethod:] (MyFile.m:15)
1   UIKit                               0x000000018a5fc2ac -[UIScrollView _willMoveToWindow:] + 172
2   UIKit                               0x000000018a5d5ca4 __85-[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:]_block_invoke + 88
3   UIKit                               0x000000018a5d5b8c -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:] + 460
4   UIKit                               0x000000018a5d5cc0 __85-[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:]_block_invoke + 116
5   UIKit                               0x000000018a5d5b8c -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:] + 460
6   UIKit                               0x000000018a5d5cc0 __85-[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:]_block_invoke + 116
7   UIKit                               0x000000018a5d5b8c -[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:] + 460
8   UIKit                               0x000000018a8e85ac __UIViewWillBeRemovedFromSuperview + 460
9   UIKit                               0x000000018a5d4abc -[UIView(Hierarchy) removeFromSuperview] + 96
10  UIKit                               0x000000018a935b7c __71-[UIPresentationController _initViewHierarchyForPresentationSuperview:]_block_invoke596 + 344
11  UIKit                               0x000000018a9306f8 -[UIPresentationController transitionDidFinish:] + 124
12  UIKit                               0x000000018aa584d8 -[_UICurrentContextPresentationController transitionDidFinish:] + 44
13  UIKit                               0x000000018a933d9c __56-[UIPresentationController runTransitionForCurrentState]_block_invoke_2 + 188
14  UIKit                               0x000000018a70b668 -[_UIViewControllerTransitionContext completeTransition:] + 116
15  UIKit                               0x000000018a70b454 -[UITransitionView notifyDidCompleteTransition:] + 252
16  UIKit                               0x000000018a70af38 -[UITransitionView _didCompleteTransition:] + 1404
17  UIKit                               0x000000018a70a9a8 -[UITransitionView _transitionDidStop:finished:] + 124
18  UIKit                               0x000000018a616d3c -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 312
19  UIKit                               0x000000018a616bc4 -[UIViewAnimationState animationDidStop:finished:] + 108
20  QuartzCore                          0x0000000189dddc2c CA::Layer::run_animation_callbacks(void*) + 284
21  libdispatch.dylib                   0x000000019a3a96a8 _dispatch_client_callout + 16
22  libdispatch.dylib                   0x000000019a3aedb0 _dispatch_main_queue_callback_4CF + 1844
23  CoreFoundation                      0x00000001850001f8 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
24  CoreFoundation                      0x0000000184ffe060 __CFRunLoopRun + 1628
25  CoreFoundation                      0x0000000184f2cca0 CFRunLoopRunSpecific + 384
26  GraphicsServices                    0x000000018ff94088 GSEventRunModal + 180
27  UIKit                               0x000000018a644ffc UIApplicationMain + 204
28  MyApp                               0x0000000100093918 main (main.m:16)
29  libdyld.dylib                       0x000000019a3da8b8 <redacted> + 4


Binary Images:
...

Если я не найду более легкий подход, это будет путь для меня пока. Может быть полезно создать скрипт, который извлекает стеки вызовов из файла журнала, удаляет начальные пробелы и вставляет их в журнал сбоев.

SYMBOLICATECRASH

Apple поставляет скрипт с XCode, который полностью ускоряет процесс символизации отчета о сбое. Если у вас есть dSYM, бинарный файл вашего приложения и отчет о сбое, это, вероятно, самый простой способ символизации. Вам не нужно беспокоиться ни об одном из адресов - этот скрипт проанализирует весь файл аварийного дампа и использует ATOS, чтобы преобразовать все адреса в символы для вас.

Найдите "symbolicatecrash" в вашей системе:

cd /Applications/Xcode.app
find . -name symbolicatecrash

Экспортируйте переменную среды DEVELOPER_DIR, если она не существует

export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"

Скопируйте двоичный файл.app, отчет о сбое и файл.dSYM во временную папку (например, ~/tmp). Запустите скрипт, как в нашем примере ниже:

/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash -v ApteligentExampleApp.crash ApteligentExampleApp.app.dSYM/

Если все идет хорошо, скрипт должен символизировать весь ваш файл сбоев и выводить результат в окно вашего терминала. Этот скрипт не делает ничего такого, что вы не могли бы сделать вручную с помощью ATOS или другого инструмента, но он даст вам то, что вам нужно гораздо быстрее.

Источник

Другие вопросы по тегам