Как "вручную" символизировать [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 или другого инструмента, но он даст вам то, что вам нужно гораздо быстрее.