Slide 1

Slide 1 text

Это Сан-Франциско с катера Mach-O, lldb, dSYM на практике Владислав Алексеев [email protected] 1

Slide 2

Slide 2 text

Программист 2

Slide 3

Slide 3 text

Почти не пишу код для iOS 3

Slide 4

Slide 4 text

Слежу за CI DevEx Чуть тестинг 4

Slide 5

Slide 5 text

В общем, всякая инфра 5

Slide 6

Slide 6 text

Что делает программист? 6

Slide 7

Slide 7 text

Программирует! 7

Slide 8

Slide 8 text

Пишет код 8

Slide 9

Slide 9 text

Пишет код Читает код 9

Slide 10

Slide 10 text

Пишет код Читает код Думает 10

Slide 11

Slide 11 text

Пишет код Читает код Думает Проверяет код 11

Slide 12

Slide 12 text

Главные инструменты 12

Slide 13

Slide 13 text

IDE 13

Slide 14

Slide 14 text

Ideal Development Environment 14

Slide 15

Slide 15 text

Ideal Development Environment Дебаггер 15

Slide 16

Slide 16 text

Ideal Development Environment Дебаггер Компилятор 16

Slide 17

Slide 17 text

Ideal Development Environment Дебаггер Компилятор SCM 17

Slide 18

Slide 18 text

Ideal Development Environment Дебаггер Компилятор SCM …….. 18

Slide 19

Slide 19 text

Поговорим о дебаггере 19

Slide 20

Slide 20 text

Основная задача дебаггера? 20

Slide 21

Slide 21 text

21

Slide 22

Slide 22 text

22

Slide 23

Slide 23 text

23 Ы

Slide 24

Slide 24 text

24 Ы Б

Slide 25

Slide 25 text

25 К Ы Б

Slide 26

Slide 26 text

26 Й К Ы Б

Slide 27

Slide 27 text

27 Й К И Ы Б

Slide 28

Slide 28 text

28 Й К И Ы Р Б

Slide 29

Slide 29 text

29 Й К И Т Ы Р Б

Slide 30

Slide 30 text

30 Й К П О И Н Т Ы Е Р Б

Slide 31

Slide 31 text

Брейкпоинты 31

Slide 32

Slide 32 text

Брейкпоинты Стектрейс 32

Slide 33

Slide 33 text

Брейкпоинты Стектрейс Переменные 33

Slide 34

Slide 34 text

Брейкпоинты Стектрейс Переменные Выполнение команд 34

Slide 35

Slide 35 text

Брейкпоинты Стектрейс Переменные Выполнение команд Логи 35

Slide 36

Slide 36 text

Брейкпоинты Стектрейс Переменные Выполнение команд Логи Расширения 36

Slide 37

Slide 37 text

Брейкпоинт 37

Slide 38

Slide 38 text

38

Slide 39

Slide 39 text

Брейкпоинт «со стороны» (lldb) b -[SimpleClass crash] Breakpoint 1: where = mytool`-[SimpleClass crash] + 35 at SimpleClass.m:14, address = 0x0000000100000d53 Установка брейкпоинта по имени метода: 39

Slide 40

Slide 40 text

Брейкпоинт «со стороны» (lldb) b SimpleClass.m:17 Breakpoint 4: where = mytool`-[SimpleClass crash] + 117 at SimpleClass.m:17, address = 0x0000000100000da5 Установка брейкпоинта в конкретной позиции в исходном файле: 40

Slide 41

Slide 41 text

Брейкпоинт «со стороны» (lldb) b /Users/vvalekseev/Developer/SimpleTool/SimpleTool/SimpleClass.m:17 Breakpoint 4: where = mytool`-[SimpleClass crash] + 117 at SimpleClass.m:17, address = 0x0000000100000da5 Установка брейкпоинта в конкретной позиции в исходном файле, Xcode-way: 41

Slide 42

Slide 42 text

Брейкпоинт «со стороны» (lldb) b /Users/vvalekseev/Developer/SimpleTool/SimpleTool/SimpleClass.m:17 Breakpoint 4: where = mytool`-[SimpleClass crash] + 117 at SimpleClass.m:17, address = 0x0000000100000da5 (lldb) b 0x0000000100000da5 Breakpoint 5: address = 0x0000000100000da5 Установка брейкпоинта по адресу: 42

Slide 43

Slide 43 text

43 Что ещё за адрес? https://www.youtube.com/watch?v=wrYFUBA2kUA

Slide 44

Slide 44 text

Как работают брейкпоинты? ❖ Аппаратно - в debug регистр записывается адрес остановки ❖ Программно - по адресу остановки подменяется инструкция ❖ Интересный доклад - http://events.linuxfoundation.org/sites/events/ files/slides/slides_16.pdf 44

Slide 45

Slide 45 text

Для установки брейкпоинта нужен адрес инструкции, где требуется остановить программу 45

Slide 46

Slide 46 text

Как происходит резолвинг адресов? 46

Slide 47

Slide 47 text

-[SimpleClass crash] SimpleClass.m:14 0x0000000100000d53 47

Slide 48

Slide 48 text

Debug information 48

Slide 49

Slide 49 text

Генерация debug info $ clang -fobjc-arc -g -c SimpleClass.m -o SimpleClass.o $ clang -fobjc-arc -g -c main.m -o main.o $ clang -fobjc-arc -fobjc-link-runtime SimpleClass.o main.o -o mytool Компиляция: Линковка: 49

Slide 50

Slide 50 text

–g 50

Slide 51

Slide 51 text

–generate 51

Slide 52

Slide 52 text

generate debug information 52

Slide 53

Slide 53 text

DWARF 53

Slide 54

Slide 54 text

DWARF 54

Slide 55

Slide 55 text

55

Slide 56

Slide 56 text

КОГДА ЧИТАЕШЬ СПЕКИ К DWARF 56

Slide 57

Slide 57 text

Спеки - ничто, dwarfdump - всё! 57

Slide 58

Slide 58 text

dwarfdump $ dwarfdump mytool -------------------------------------------------------- File: mytool (x86_64) -------------------------------------------------------- .debug_info contents: < EMPTY > 58

Slide 59

Slide 59 text

Mach-O 59

Slide 60

Slide 60 text

Mach-O 0xCAFEBABE/0xFEEDFACE Header Load Command Load Command String Table Signature 60 Load Command

Slide 61

Slide 61 text

loader.h struct mach_header { uint32_t magic; /* mach magic number identifier */ cpu_type_t cputype; /* cpu specifier */ cpu_subtype_t cpusubtype; /* machine specifier */ uint32_t filetype; /* type of file */ uint32_t ncmds; /* number of load commands */ uint32_t sizeofcmds; /* the size of all the load commands */ uint32_t flags; /* flags */ }; #define MH_MAGIC 0xfeedface /* the mach magic number */ #define MH_CIGAM 0xcefaedfe /* NXSwapInt(MH_MAGIC) */ 61

Slide 62

Slide 62 text

loader.h struct load_command { uint32_t cmd; /* type of load command */ uint32_t cmdsize; /* total size of command in bytes */ }; 62

Slide 63

Slide 63 text

loader.h #define LC_SEGMENT 0x1 /* segment of this file to be mapped */ #define LC_SYMTAB 0x2 /* link-edit stab symbol table info */ #define LC_SYMSEG 0x3 /* link-edit gdb symbol table info (obsolete) */ #define LC_THREAD 0x4 /* thread */ #define LC_UNIXTHREAD 0x5 /* unix thread (includes a stack) */ #define LC_LOADFVMLIB 0x6 /* load a specified fixed VM shared library */ #define LC_IDFVMLIB 0x7 /* fixed VM shared library identification */ #define LC_IDENT 0x8 /* object identification info (obsolete) */ #define LC_FVMFILE 0x9 /* fixed VM file inclusion (internal use) */ #define LC_PREPAGE 0xa /* prepage command (internal use) */ #define LC_DYSYMTAB 0xb /* dynamic link-edit symbol table info */ #define LC_LOAD_DYLIB 0xc /* load a dynamically linked shared library */ #define LC_ID_DYLIB 0xd /* dynamically linked shared lib ident */ #define LC_LOAD_DYLINKER 0xe /* load a dynamic linker */ #define LC_ID_DYLINKER 0xf /* dynamic linker identification */ #define LC_PREBOUND_DYLIB 0x10 /* modules prebound for a dynamically */ … 63

Slide 64

Slide 64 text

loader.h struct symtab_command { uint32_t cmd; /* LC_SYMTAB */ uint32_t cmdsize; /* sizeof(struct symtab_command) */ uint32_t symoff; /* symbol table offset */ uint32_t nsyms; /* number of symbol table entries */ uint32_t stroff; /* string table offset */ uint32_t strsize; /* string table size in bytes */ }; 64 struct load_command { uint32_t cmd; uint32_t cmdsize; };

Slide 65

Slide 65 text

struct symtab_command { uint32_t cmd; /* LC_SYMTAB */ uint32_t cmdsize; /* sizeof(struct symtab_command) */ uint32_t symoff; /* symbol table offset */ uint32_t nsyms; /* number of symbol table entries */ uint32_t stroff; /* string table offset */ uint32_t strsize; /* string table size in bytes */ }; struct load_command { uint32_t cmd; uint32_t cmdsize; }; loader.h 65

Slide 66

Slide 66 text

loader.h struct symtab_command { uint32_t cmd; /* LC_SYMTAB */ uint32_t cmdsize; /* sizeof(struct symtab_command) */ uint32_t symoff; /* symbol table offset */ uint32_t nsyms; /* number of symbol table entries */ uint32_t stroff; /* string table offset */ uint32_t strsize; /* string table size in bytes */ }; 66

Slide 67

Slide 67 text

loader.h struct symtab_command { uint32_t cmd; /* LC_SYMTAB */ uint32_t cmdsize; /* sizeof(struct symtab_command) */ uint32_t symoff; /* symbol table offset */ uint32_t nsyms; /* number of symbol table entries */ uint32_t stroff; /* string table offset */ uint32_t strsize; /* string table size in bytes */ }; 67

Slide 68

Slide 68 text

Mach-O $ nm -a mytool 0000000000000000 - 01 0000 SO 0000000000000000 - 01 0000 SO 0000000000000074 - 00 0000 FUN 0000000000000074 - 01 0000 ENSYM 000000000000007f - 01 0000 ENSYM 000000000000007f - 00 0000 FUN 0000000100000de0 - 01 0000 BNSYM 0000000100000e60 - 01 0000 BNSYM 0000000100000de0 t -[SimpleClass crash] 0000000100000de0 - 01 0000 FUN -[SimpleClass crash] 0000000000000000 - 00 0000 SO /Users/vvalekseev/Developer/SimpleTool/ SimpleTool/ 0000000000000000 - 00 0000 SO /Users/vvalekseev/Developer/SimpleTool/ SimpleTool/ 000000005a0c056a - 03 0001 OSO /Users/vvalekseev/Developer/SimpleTool/ SimpleTool/SimpleClass.o 000000005a0c0572 - 03 0001 OSO /Users/vvalekseev/Developer/SimpleTool/ SimpleTool/main.o 68

Slide 69

Slide 69 text

Mach-O 0000000000000074 - 00 0000 FUN 0000000000000074 - 01 0000 ENSYM 000000000000007f - 01 0000 ENSYM 000000000000007f - 00 0000 FUN 0000000100000de0 - 01 0000 BNSYM 0000000100000e60 - 01 0000 BNSYM 0000000100000de0 t -[SimpleClass crash] 0000000100000de0 - 01 0000 FUN -[SimpleClass crash] 0000000000000000 - 00 0000 SO /Users/vvalekseev/Developer/SimpleTool/ SimpleTool/ 0000000000000000 - 00 0000 SO /Users/vvalekseev/Developer/SimpleTool/ SimpleTool/ 000000005a0c056a - 03 0001 OSO /Users/vvalekseev/Developer/SimpleTool/ SimpleTool/SimpleClass.o 000000005a0c0572 - 03 0001 OSO /Users/vvalekseev/Developer/SimpleTool/ SimpleTool/main.o 0000000000000000 - 00 0000 SO SimpleClass.m U _OBJC_CLASS_$_NSException U _OBJC_CLASS_$_NSObject 0000000000000000 - 00 0000 GSYM _OBJC_CLASS_$_SimpleClass 00000001000011a0 S _OBJC_CLASS_$_SimpleClass U _OBJC_METACLASS_$_NSObject 0000000000000000 - 00 0000 GSYM _OBJC_METACLASS_$_SimpleClass 0000000100001178 S _OBJC_METACLASS_$_SimpleClass U ___CFConstantStringClassReference 69

Slide 70

Slide 70 text

Mach-O binary main.o SimpleClass.o libSome.a(SomeClass.o) libSome.a(HelperClass.o) 70

Slide 71

Slide 71 text

dwarfdump $ dwarfdump SimpleClass.o ---------------------------------------------------------------------- File: SimpleClass.o (x86_64) ---------------------------------------------------------------------- .debug_info contents: 0x00000000: Compile Unit: length = 0x000000b3 version = 0x0004 abbr_offset = 0x00000000 addr_size = 0x08 (next CU at 0x000000b7) 0x0000000b: TAG_compile_unit [1] * AT_producer( "Apple LLVM version 9.0.0 (clang-900.0.38)" ) AT_language( DW_LANG_ObjC ) AT_name( "SimpleClass.m" ) AT_stmt_list( 0x00000000 ) AT_comp_dir( "/Users/vvalekseev/Developer/SimpleTool/SimpleTool" ) AT_APPLE_major_runtime_vers( 0x02 ) AT_low_pc( 0x0000000000000000 ) AT_high_pc( 0x00000074 ) 71

Slide 72

Slide 72 text

dwarfdump 0x0000005d: TAG_pointer_type [7] AT_type( {0x00000062} ( objc_class ) ) 0x00000062: TAG_structure_type [8] AT_name( "objc_class" ) AT_declaration( true ) 0x00000067: TAG_subprogram [9] * AT_low_pc( 0x0000000000000000 ) AT_high_pc( 0x00000074 ) AT_frame_base( rbp ) AT_object_pointer( {0x00000080} ) AT_name( "-[SimpleClass crash]" ) AT_decl_file( "/Users/vvalekseev/Developer/SimpleTool/ SimpleTool/SimpleClass.m" ) AT_decl_line( 13 ) AT_prototyped( true ) 0x00000080: TAG_formal_parameter [10] AT_location( fbreg -8 ) AT_name( "self" ) AT_type( {0x00000099} ( const SimpleClass* ) ) AT_artificial( true ) 72

Slide 73

Slide 73 text

dwarfdump 0x0000005d: TAG_pointer_type [7] AT_type( {0x00000062} ( objc_class ) ) 0x00000062: TAG_structure_type [8] AT_name( "objc_class" ) AT_declaration( true ) 0x00000067: TAG_subprogram [9] * AT_low_pc( 0x0000000000000000 ) AT_high_pc( 0x00000074 ) AT_frame_base( rbp ) AT_object_pointer( {0x00000080} ) AT_name( "-[SimpleClass crash]" ) AT_decl_file( "/Users/vvalekseev/Developer/SimpleTool/ SimpleTool/SimpleClass.m" ) AT_decl_line( 13 ) AT_prototyped( true ) 0x00000080: TAG_formal_parameter [10] AT_location( fbreg -8 ) AT_name( "self" ) AT_type( {0x00000099} ( const SimpleClass* ) ) AT_artificial( true ) 73

Slide 74

Slide 74 text

0x00000067: TAG_subprogram [9] * AT_low_pc( 0x0000000000000000 ) AT_high_pc( 0x00000074 ) AT_frame_base( rbp ) AT_object_pointer( {0x00000080} ) AT_name( "-[SimpleClass crash]" ) AT_decl_file( "/Users/vvalekseev/Developer/SimpleTool/ SimpleTool/SimpleClass.m" ) AT_decl_line( 13 ) AT_prototyped( true ) 0x00000080: TAG_formal_parameter [10] AT_location( fbreg -8 ) AT_name( "self" ) AT_type( {0x00000099} ( const SimpleClass* ) ) AT_artificial( true ) 0x0000008c: TAG_formal_parameter [10] AT_location( fbreg -16 ) AT_name( "_cmd" ) AT_type( {0x000000a3} ( SEL ) ) AT_artificial( true ) 0x00000098: NULL 74

Slide 75

Slide 75 text

Так как же происходит поиск адреса для ? b SimpleClass.m:17 75

Slide 76

Slide 76 text

Так как же происходит поиск адреса для ? b SimpleClass.m:17 Ответ в секции debug_line. 76

Slide 77

Slide 77 text

$ dwarfdump --debug-line SimpleClass.o ---------------------------------------------------------------------- File: SimpleClass.o (x86_64) ---------------------------------------------------------------------- .debug_line contents: ---------------------------------------------------------------------- debug_line[0x00000000] ---------------------------------------------------------------------- Line table prologue: total_length: 0x000000a9 version: 0x0002 prologue_length: 0x00000057 min_inst_length: 0x01 default_is_stmt: 0x01 line_base: -5 line_range: 14 opcode_base: 0x0d standard_opcode_lengths[ DW_LNS_copy ] = 0 standard_opcode_lengths[ DW_LNS_advance_pc ] = 1 standard_opcode_lengths[ DW_LNS_advance_line ] = 1 standard_opcode_lengths[ DW_LNS_set_file ] = 1 standard_opcode_lengths[ DW_LNS_set_column ] = 1 standard_opcode_lengths[ DW_LNS_negate_stmt ] = 0 77

Slide 78

Slide 78 text

$ dwarfdump --debug-line SimpleClass.o ---------------------------------------------------------------------- File: SimpleClass.o (x86_64) ---------------------------------------------------------------------- .debug_line contents: ---------------------------------------------------------------------- debug_line[0x00000000] ---------------------------------------------------------------------- Line table prologue: total_length: 0x000000a9 version: 0x0002 prologue_length: 0x00000057 min_inst_length: 0x01 default_is_stmt: 0x01 line_base: -5 line_range: 14 opcode_base: 0x0d standard_opcode_lengths[ DW_LNS_copy ] = 0 standard_opcode_lengths[ DW_LNS_advance_pc ] = 1 standard_opcode_lengths[ DW_LNS_advance_line ] = 1 standard_opcode_lengths[ DW_LNS_set_file ] = 1 standard_opcode_lengths[ DW_LNS_set_column ] = 1 standard_opcode_lengths[ DW_LNS_negate_stmt ] = 0 78

Slide 79

Slide 79 text

file_names[ 1] 0 0x00000000 0x00000000 SimpleClass.m file_names[ 2] 1 0x00000000 0x00000000 NSObject.h file_names[ 3] 2 0x00000000 0x00000000 SimpleClass.h 0x00000061: DW_LNE_set_address( 0x0000000000000000 ) 0x0000006c: DW_LNS_advance_line( 12 ) 0x0000006e: DW_LNS_copy 0x0000000000000000 1 13 0 is_stmt 0x0000006f: DW_LNS_set_column( 6 ) 0x00000071: DW_LNS_set_prologue_end 0x00000072: DW_LNS_advance_pc( 35 ) 0x00000074: address += 0, line += 1 0x0000000000000023 1 14 6 is_stmt prologue_end 0x00000075: DW_LNE_set_discriminator( 1 ) 0x00000079: DW_LNS_negate_stmt 0x0000007a: DW_LNS_const_add_pc( 0x0000000000000011 ) 0x0000007b: address += 10, line += 0 0x0000000000000044 1 14 6 0x0000007c: DW_LNS_set_column( 5 ) 0x0000007e: address += 8, line += 0 0x000000000000004c 1 14 5 0x0000007f: DW_LNE_set_discriminator( 2 ) 79

Slide 80

Slide 80 text

0x0000007f: DW_LNE_set_discriminator( 2 ) 0x00000083: address += a, line += 0 0x0000000000000056 1 14 5 0x00000084: address += c, line += 0 0x0000000000000062 1 14 5 0x00000085: DW_LNE_set_discriminator( 3 ) 0x00000089: address += 4, line += 0 0x0000000000000066 1 14 5 0x0000008a: DW_LNS_negate_stmt 0x0000008b: address += f, line += 3 0x0000000000000075 1 17 5 is_stmt 0x0000008c: DW_LNS_set_column( 9 ) 0x0000008e: address += a, line += 1 0x000000000000007f 1 18 9 is_stmt 0x0000008f: DW_LNS_set_column( 6 ) 0x00000091: address += 7, line += 1 0x0000000000000086 1 19 6 is_stmt 0x00000092: DW_LNS_set_column( 1 ) 80

Slide 81

Slide 81 text

0x0000008a: DW_LNS_negate_stmt 0x0000008b: address += f, line += 3 0x0000000000000075 1 17 5 is_stmt 81

Slide 82

Slide 82 text

0x0000008a: DW_LNS_negate_stmt 0x0000008b: address += f, line += 3 0x0000000000000075 1 17 5 is_stmt $ dwarfdump -a SimpleClass.o ---------------------------------------------------------------------- File: SimpleClass.o (x86_64) ---------------------------------------------------------------------- 0x00000067: [0x0000000000000000 - 0x0000000000000098) -[SimpleClass crash] 0x000000a7: [0x00000000000000a0 - 0x00000000000000d1) -[SimpleClass anotherMethod] Адрес строки 17 внутри файла SimpleClass.o: Адреса всех функций внутри SimpleClass.o: 82

Slide 83

Slide 83 text

$ dwarfdump -a -e SimpleClass.o ---------------------------------------------------------------------- File: SimpleClass.o (x86_64) ---------------------------------------------------------------------- 0x00000067: [0x0000000000000000 - 0x0000000000000098) -[SimpleClass crash] 0x000000a7: [0x00000000000000a0 - 0x00000000000000d1) -[SimpleClass anotherMethod] Адреса всех функций внутри SimpleClass.o: Файл начинается с -[SimpleClass crash]. Внутри слинкованого бинарника функция имеет адрес: $ nm -a mytool 0000000000000000 - 01 0000 SO 0000000000000000 - 01 0000 SO 0000000000000031 - 00 0000 FUN 0000000000000031 - 01 0000 ENSYM 000000000000007f - 01 0000 ENSYM 000000000000007f - 00 0000 FUN 00000000000000a0 - 00 0000 FUN 83

Slide 84

Slide 84 text

Файл начинается с -[SimpleClass crash]. Внутри слинкованого бинарника функция имеет адрес: $ nm -a mytool 0000000000000000 - 01 0000 SO 0000000000000000 - 01 0000 SO 0000000000000031 - 00 0000 FUN 0000000000000031 - 01 0000 ENSYM 000000000000007f - 01 0000 ENSYM 000000000000007f - 00 0000 FUN 00000000000000a0 - 00 0000 FUN 00000000000000a0 - 01 0000 ENSYM 0000000100000d30 - 01 0000 BNSYM 0000000100000dd0 - 01 0000 BNSYM 0000000100000e10 - 01 0000 BNSYM 0000000100000dd0 t -[SimpleClass anotherMethod] 0000000100000dd0 - 01 0000 FUN -[SimpleClass anotherMethod] 0000000100000d30 t -[SimpleClass crash] 0000000100000d30 - 01 0000 FUN -[SimpleClass crash] 0000000000000000 - 00 0000 SO /Users/vvalekseev/Developer/SimpleTool/ SimpleTool/ 0000000000000000 - 00 0000 SO /Users/vvalekseev/Developer/SimpleTool/ SimpleTool/ 84

Slide 85

Slide 85 text

0000000100000d30 - 01 0000 FUN -[SimpleClass crash] + 0000000000000075 = 0x00000100000DA5 Значит, брейкпоинт ставим по адресу: 85

Slide 86

Slide 86 text

Проверяем через LLDB: $ lldb (lldb) target create mytool Current executable set to 'mytool' (x86_64). 86

Slide 87

Slide 87 text

Проверяем через LLDB: $ lldb (lldb) target create mytool Current executable set to 'mytool' (x86_64). (lldb) b SimpleClass.m:17 Breakpoint 1: where = mytool`-[SimpleClass crash] + 117 at SimpleClass.m:17, address = 0x0000000100000da5 87

Slide 88

Slide 88 text

88 0x00000100000DA5 == 0x0000000100000da5

Slide 89

Slide 89 text

Адреса сходятся! 89

Slide 90

Slide 90 text

Аналогично дебаггер подцепляет контекст из исходного кода: (lldb) b -[SimpleClass crash] Breakpoint 1: where = mytool`-[SimpleClass crash] + 35 at SimpleClass.m:14, address = 0x0000000100000d53 (lldb) run Process 5325 launched: '/Users/vvalekseev/Developer/SimpleTool/SimpleTool/ mytool' (x86_64) Process 5325 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 frame #0: 0x0000000100000d53 mytool`-[SimpleClass crash] (self=0x0000000100406720, _cmd="crash") at SimpleClass.m:14 11 @implementation SimpleClass 12 13 - (void)crash { -> 14 [[NSException exceptionWithName:@"ExceptionName" 15 reason:@"Reason!" 16 userInfo:nil] raise]; 17 NSLog(@"I raised an exception!"); Target 0: (mytool) stopped. 90

Slide 91

Slide 91 text

Stripped binary 91

Slide 92

Slide 92 text

Т.е. без дебаг информации 92

Slide 93

Slide 93 text

Но что попадает в бинарник, если dwarfdump выдает пустоту? 93

Slide 94

Slide 94 text

- Пути до исходников - Названия методов - И прочее 94

Slide 95

Slide 95 text

MachOView.app 95

Slide 96

Slide 96 text

MachOView.app 96

Slide 97

Slide 97 text

Stripping $ strip mytool -o mytool.stripped 97

Slide 98

Slide 98 text

Stripping 98

Slide 99

Slide 99 text

Stripping 99

Slide 100

Slide 100 text

Ready for Upload 100

Slide 101

Slide 101 text

А как же крешлоги? 101

Slide 102

Slide 102 text

Крешлоги $ ./mytool.stripped 2017-11-15 14:34:53.100 mytool.stripped[5385:4492270] *** Terminating app due to uncaught exception 'ExceptionName', reason: 'Reason!' *** First throw call stack: ( 0 CoreFoundation 0x00007fff56f922fb __exceptionPreprocess + 171 1 libobjc.A.dylib 0x00007fff7d903c76 objc_exception_throw + 48 2 CoreFoundation 0x00007fff56f92249 -[NSException raise] + 9 3 mytool.stripped 0x000000010facfd92 mytool.stripped + 3474 4 mytool.stripped 0x000000010facfe6e mytool.stripped + 3694 5 libdyld.dylib 0x00007fff7e4f2145 start + 1 6 ??? 0x0000000000000001 0x0 + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException Abort trap: 6 102

Slide 103

Slide 103 text

Нужно держать дебаг инфо под рукой 103

Slide 104

Slide 104 text

Сохранить нестрипаный бинарник Все .o, .a файлы 104

Slide 105

Slide 105 text

Или сгенерировать dSYM 105

Slide 106

Slide 106 text

dsymutil $ dsymutil mytool 106

Slide 107

Slide 107 text

dsymutil $ dsymutil mytool $ dwarfdump -a mytool.dSYM/ ---------------------------------------------------------------------- File: mytool.dSYM/Contents/Resources/DWARF/mytool (x86_64) ---------------------------------------------------------------------- 0x00000067: [0x0000000100000d30 - 0x0000000100000dc8) -[SimpleClass crash] 0x000000a7: [0x0000000100000dd0 - 0x0000000100000e01) -[SimpleClass anotherMethod] 0x00000137: [0x0000000100000e10 - 0x0000000100000e8f) main .debug_abbrev contents: Abbrev table for offset: 0x00000000 ... 107

Slide 108

Slide 108 text

Невидимая связь $ dwarfdump --uuid mytool UUID: 65C365E2-5955-3AF1-B12E-1403E3B3ACF4 (x86_64) mytool $ dwarfdump --uuid mytool.dSYM/ UUID: 65C365E2-5955-3AF1-B12E-1403E3B3ACF4 (x86_64) mytool.dSYM/Contents/ Resources/DWARF/mytool 108

Slide 109

Slide 109 text

Невидимая связь LLDB не загрузит dSYM, если UUID не совпадают: $ lldb (lldb) target create mytool.stripped.wronguuid (lldb) target symbols add mytool.dSYM/Contents/Resources/DWARF/mytool error: symbol file '/Users/vvalekseev/Developer/SimpleTool/SimpleTool/ mytool.dSYM/Contents/Resources/DWARF/mytool' does not match any existing module 109

Slide 110

Slide 110 text

ТРЕБУЙТЕ ГЕНЕРАЦИИ DSYM ПРИ РЕЛИЗЕ 110

Slide 111

Slide 111 text

При поступлении крешлога 111

Slide 112

Slide 112 text

Крешлог Проверьте UUID бинарника (и dSYM-а) из крешрепорта: 112

Slide 113

Slide 113 text

Создать debug target с бинарником Подгрузить нужный dSYM Подключить скпипт crashlog Символизировать крешлог

Slide 114

Slide 114 text

Крешлог LLDB умеет символизировать крешлоги: (lldb) target create mytool.stripped Current executable set to 'mytool.stripped' (x86_64). 114

Slide 115

Slide 115 text

Крешлог LLDB умеет символизировать крешлоги: (lldb) target create mytool.stripped Current executable set to 'mytool.stripped' (x86_64). (lldb) target symbols add mytool.dSYM/Contents/Resources/DWARF/mytool symbol file '/Users/vvalekseev/Developer/SimpleTool/SimpleTool/mytool.dSYM/ Contents/Resources/DWARF/mytool' has been added to '/Users/vvalekseev/ Developer/SimpleTool/SimpleTool/mytool.stripped' 115

Slide 116

Slide 116 text

Крешлог LLDB умеет символизировать крешлоги: (lldb) target create mytool.stripped Current executable set to 'mytool.stripped' (x86_64). (lldb) target symbols add mytool.dSYM/Contents/Resources/DWARF/mytool symbol file '/Users/vvalekseev/Developer/SimpleTool/SimpleTool/mytool.dSYM/ Contents/Resources/DWARF/mytool' has been added to '/Users/vvalekseev/ Developer/SimpleTool/SimpleTool/mytool.stripped' (lldb) command script import lldb.macosx.crashlog "crashlog" and "save_crashlog" command installed, use the "--help" option for detailed help "malloc_info", "ptr_refs", "cstr_refs", "find_variable", and "objc_refs" commands have been installed, use the "--help" options on these commands for detailed help. 116

Slide 117

Slide 117 text

Крешлог LLDB умеет символизировать крешлоги: (lldb) crashlog /Users/vvalekseev/Developer/SimpleTool/SimpleTool/log.crash Getting symbols for 65C365E2-5955-3AF1-B12E-1403E3B3ACF4 /Users/vvalekseev/ Developer/SimpleTool/SimpleTool/mytool.stripped... ok Application Specific Backtrace[1] [ 0] 0x00007fff56f922fb CoreFoundation`__exceptionPreprocess + 171 0x00007fff56f922e9: callq 0x8b100 ; __CFLookUpClass 0x00007fff56f922ee: movq 0x4cf5fb(%rip), %rsi ; "" 0x00007fff56f922f5: movq %rax, %rdi 0x00007fff56f922f8: callq *%r12 -> 0x00007fff56f922fb: movq %rax, %r14 0x00007fff56f922fe: testq %r15, %r15 0x00007fff56f92301: je 0x10b325 ; <+213> 0x00007fff56f92303: movq 0x4d1006(%rip), %rax ; NSException.reserved 0x00007fff56f9230a: movq (%rbx,%rax), %rdi 117

Slide 118

Slide 118 text

0x00007fff56f922fe: testq %r15, %r15 0x00007fff56f92301: je 0x10b325 ; <+213> 0x00007fff56f92303: movq 0x4d1006(%rip), %rax ; NSException.reserved 0x00007fff56f9230a: movq (%rbx,%rax), %rdi [ 1] 0x00007fff7d903c75 libobjc.A.dylib`objc_exception_throw + 47 [ 2] 0x00007fff56f92248 CoreFoundation`-[NSException raise] + 8 [ 3] 0x0000000102de4d91 mytool.stripped`-[SimpleClass crash] + 97 at SimpleClass.m:14:5 [ 4] 0x0000000102de4e6d mytool.stripped`main + 93 at main.m:10:9 [ 5] 0x00007fff7e4f2144 libdyld.dylib`start Thread[0] EXC_CRASH (SIGABRT) (0x0000000000000000, 0x0000000000000000) [ 0] 0x00007fff7e641fce libsystem_kernel.dylib`__pthread_kill + 10 0x00007fff7e641fc4: movl $0x2000148, %eax ; imm = 0x2000148 0x00007fff7e641fc9: movq %rcx, %r10 0x00007fff7e641fcc: syscall -> 0x00007fff7e641fce: jae 0x1bfd8 ; <+20> 0x00007fff7e641fd0: movq %rax, %rdi 0x00007fff7e641fd3: jmp 0x1376c ; cerror_nocancel 0x00007fff7e641fd8: retq 0x00007fff7e641fd9: nop [ 1] 0x00007fff7e77f14f libsystem_pthread.dylib`pthread_kill + 332 118

Slide 119

Slide 119 text

А еще можно Drag&Drop-нуть крешлог в Xcode 119

Slide 120

Slide 120 text

dSYM Ecosystem 120

Slide 121

Slide 121 text

dSYM Ecosystem ❖ http://wiki.dwarfstd.org/index.php? title=Apple%27s_%22Lazy%22_DWARF_Scheme ❖ https://lldb.llvm.org/symbols.html 121

Slide 122

Slide 122 text

Поговорим о проблемах 122

Slide 123

Slide 123 text

1. Мегалайфхак - «просто выключи dSYM» 123

Slide 124

Slide 124 text

dsymutil медленный 124

Slide 125

Slide 125 text

Но не выключайте dSYM для Release конфигурации 125

Slide 126

Slide 126 text

126

Slide 127

Slide 127 text

2. dSYM и Bitcode 127

Slide 128

Slide 128 text

Компиляция происходит на стороне Apple 128

Slide 129

Slide 129 text

Генерация dSYM там же 129

Slide 130

Slide 130 text

dSYM можно скачать в iTunes Connect 130

Slide 131

Slide 131 text

3. Генерация лишних dSYM 131

Slide 132

Slide 132 text

Amimono - плагин для CocoaPods 132

Slide 133

Slide 133 text

Превращает Dynamic Framework в Static Library 133

Slide 134

Slide 134 text

134 Framework1.framework Framework2.framework Framework1.framework.dSYM Framework2.framework.dSYM Binary Binary.dSYM Без amimono:

Slide 135

Slide 135 text

135 Framework1.framework Framework2.framework Framework1.framework.dSYM Framework2.framework.dSYM Binary Binary.dSYM С amimono: Framework1.framework Framework2.framework

Slide 136

Slide 136 text

136 Framework1.framework Framework2.framework Framework1.framework.dSYM Framework2.framework.dSYM Binary Binary.dSYM С amimono: Framework1.framework Framework2.framework

Slide 137

Slide 137 text

137 Framework1.framework Framework2.framework Framework1.framework.dSYM Framework2.framework.dSYM Binary Binary.dSYM С amimono: Framework1.framework Framework2.framework UUID1 UUID2 UUID3

Slide 138

Slide 138 text

138 Framework1.framework Framework2.framework Framework1.framework.dSYM Framework2.framework.dSYM Binary С amimono: Framework1.framework Framework2.framework UUID1 UUID2 UUID3 Binary.dSYM

Slide 139

Slide 139 text

139 Binary С amimono: Binary.dSYM

Slide 140

Slide 140 text

140 -280Мб со сборки в TestFlight

Slide 141

Slide 141 text

141 -280Мб со сборки в TestFlight 7 сборок в день

Slide 142

Slide 142 text

142 -280Мб со сборки в TestFlight 7 сборок в день -2Гб в день

Slide 143

Slide 143 text

4. Дебаг информация покажет большие функции 143

Slide 144

Slide 144 text

Простой скрипт! 144

Slide 145

Slide 145 text

145

Slide 146

Slide 146 text

https://github.com/beefon/ MachOListCompiledMethodSizes 146

Slide 147

Slide 147 text

5. dSYM + CocoaPods 147

Slide 148

Slide 148 text

148 Полностью выключаем dSYM для Pod-ов: post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| config.build_settings['DEBUG_INFORMATION_FORMAT'] = 'dwarf' end end

Slide 149

Slide 149 text

149 Выключаем dSYM для не-Release сборок у основного таргета: post_install do |installer| installer.aggregate_targets.each do |target| target.xcconfigs.each do |config_name, config_file| if ['Debug', 'Test'].include? config_name config_file.attributes['DEBUG_INFORMATION_FORMAT'] = 'dwarf' config_file.attributes['ONLY_ACTIVE_ARCH'] = 'YES' elsif ['Enterprise', 'Release'].include? config_name config_file.attributes['DEBUG_INFORMATION_FORMAT'] = 'dwarf-with-dsym' config_file.attributes['ONLY_ACTIVE_ARCH'] = 'NO' else raise "Unknown configuration: " + config_name end xcconfig_path = target.xcconfig_path(config_name) config_file.save_as(xcconfig_path) end end

Slide 150

Slide 150 text

150 Выключаем dSYM для не-Release сборок у основного таргета: post_install do |installer| installer.aggregate_targets.each do |target| target.xcconfigs.each do |config_name, config_file| if ['Debug', 'Test'].include? config_name config_file.attributes['DEBUG_INFORMATION_FORMAT'] = 'dwarf' config_file.attributes['ONLY_ACTIVE_ARCH'] = 'YES' elsif ['Enterprise', 'Release'].include? config_name config_file.attributes['DEBUG_INFORMATION_FORMAT'] = 'dwarf-with-dsym' config_file.attributes['ONLY_ACTIVE_ARCH'] = 'NO' else raise "Unknown configuration: " + config_name end xcconfig_path = target.xcconfig_path(config_name) config_file.save_as(xcconfig_path) end end

Slide 151

Slide 151 text

151 Выключаем dSYM для не-Release сборок у основного таргета: post_install do |installer| installer.aggregate_targets.each do |target| target.xcconfigs.each do |config_name, config_file| if ['Debug', 'Test'].include? config_name config_file.attributes['DEBUG_INFORMATION_FORMAT'] = 'dwarf' config_file.attributes['ONLY_ACTIVE_ARCH'] = 'YES' elsif ['Enterprise', 'Release'].include? config_name config_file.attributes['DEBUG_INFORMATION_FORMAT'] = 'dwarf-with-dsym' config_file.attributes['ONLY_ACTIVE_ARCH'] = 'NO' else raise "Unknown configuration: " + config_name end xcconfig_path = target.xcconfig_path(config_name) config_file.save_as(xcconfig_path) end end

Slide 152

Slide 152 text

Recap ❖ Проверьте ваши настройки генерации dSYM - не нужен для Debug! ❖ Прогоните скриптик - может, есть возможность уменьшить бинарник ❖ Амимоно - нестрашный зверь, но стоит использовать по-умному 152

Slide 153

Slide 153 text

Ссылки ❖ Доклад про дебаггинг под Линуксом: http://events.linuxfoundation.org/ sites/events/files/slides/slides_16.pdf ❖ Apple’s Lazy DWARF Scheme: http://wiki.dwarfstd.org/index.php? title=Apple%27s_%22Lazy%22_DWARF_Scheme ❖ Точки врезания в LLDB для поддержки Lazy DWARF Scheme: https://lldb.llvm.org/symbols.html

Slide 154

Slide 154 text

КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ КОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦКОНЕЦ 154