Яндекс.Метрика

    Песочница

    Как по CEE дампу найти строку в которой падает программа на майнфрейме

    Когда-то мне пришлось осваивать работу на С++ с майнфреймом, и возникла проблема как разобраться, в каком месте программа падает и в чем причина. Сразу оговоюсь, что все относится к программированию на майнфреймах под операционной системой z/OS в USS. С одной стороны получается элементарщина, но найти все это в документации IBM не так просто. Кроме того, нужно хотя бы уметь читать HLASM.

    В нижеприведенном документе я попробую описать, как по CEE дампу найти строку в которой программа падает.
    Ниже короткая программа, которая написаны так, что она обязательно упадет, как только ее запустят. Я не стал усложнять программу, и для С++ программистов она покажется слишком простой, но задача не найти ошибку сразу в коде, а прийти к ней через листинги. В реальности программы намного больше и длинее.

    int f1(int a, char *b)
    {
    char *value = 0L;
    value[0] = '\0';
    return(0);
    }

    int main()
    {
    f1(1, "");
    return(1);
    }


    Что бы найти строку, нужно программу скомпилировать с листингом, делается это так

    1. без XPLINK
    c++ -Wc,list'(t1.list)' -c t1.C
    c++ -o t1 t1.o


    2. c XPLINK
    c++ -Wc,xplink -Wc,list'(t2.list)' -c t1.C
    c++ -Wl,xplink -o t2 t1.o


    Все дело в ключе -Wc,list'(t1.list)', он заставляет сгенерировать листинг для этого файла.
    В Makefile это добавляется так.
    # Here is where we get convlit(iso8859-1) and __LIBASCII
    #
    XCCFLAGS = $(CCFLAGS_ASCII) -Wc,list'($*.list)'
    XCPPFLAGS = $(CPPFLAGS_ASCII) -Wc,list'($*.list)'


    Первая строка для програм на С, вторая на С++.
    Запускаем программу, получили дамп и сообщения ниже.

    /u/mddegt/sb/omni/cpp/test:>t1
    CEE3204S The system detected a protection exception (System Completion Code=0C4).
             From entry point f1(int,char*) at compile unit offset +0000002A at entry offset +0000002A at address 1000A95A.
    Segmentation fault
     
    /u/mddegt/sb/omni/cpp/test:>t2
    CEE3204S The system detected a protection exception (System Completion Code=0C4).
             From entry point f1(int,char*) at compile unit offset +00000016 at entry offset +00000016 at address 100063D6.
    Segmentation fault
    


    Если далеко не вдаваться в подробности, то уже тут у нас есть нужная нам информация. Мы видим, что программа упала в функции f1, и у нас есть смещение (offset). Теперь нам нужен листинг, который сгенерировался в том же каталоге, где и исходник (t1.list, t2.list. t1 для программы без XPLINK, а t2 — для программы с XPLINK).

    В листинге ищем начало функции, в которой программа упала.

    1. без XPLINK

    15694A01 V1 R6 z/OS C++                                                 t1.C: f1(int,char*)       02/14/06 15:42:32            3
     
     OFFSET OBJECT CODE        LINE#  FILE#    P S E U D O   A S S E M B L Y   L I S T I N G 
                               000001 |       *  void f1();
                               000002 |       *  
                               000003 |       *  int f1(int a, char *b) 
                                              f1(int,char*)
     000018                    000003 |                 DS    0D
     000018  47F0  F001        000003 |                 B     1(,r15)
     00001C  01C3C5C5                                         CEE eyecatcher
     000020  000000C8                                         DSA size
     000024  000000D8                                         =A(PPA1-f1(int,char*))
     000028  5050  D028        000003 |                 ST    r5,40(,r13)
     00002C  5850  D04C        000003 |                 L     r5,76(,r13)
     000030                    End of Prolog
    

    2. с XPLINK

    15694A01 V1 R6 z/OS C++                                                 t1.C: f1(int,char*)       02/14/06 16:02:24            3
     
     OFFSET OBJECT CODE        LINE#  FILE#    P S E U D O   A S S E M B L Y   L I S T I N G                                        
     
                               000001 |       *  void f1();
                               000002 |       *  
                               000003 |       *  int f1(int a, char *b) 
     
     000018                                    @1L0     DS    0D
     000018  00C300C5                                         =F'12779717'       XPLink entrypoint marker
     00001C  00C500F1                                         =F'12910833'       
     000020  00000090                                         =F'144'            
     000024  00000088                                         =F'136'            
                                              f1(int,char*)
     000028                    000003 |                 DS    0D
     000028  9067  4788        000003 |                 STM   r6,r7,1928(r4)
     00002C                    End of Prolog
    

    Первый столбец — это смещение, всторой это код, который был сгенерирован, третий — номер строки в исходном файле.
    Как видим, простая программа, без XPLINK начинается сразу, и у нее получилось смещение 000018, и функции с XPLINK сначала идет заголовок (синий), а потом уже программа, и начинается она со смещения 000028. Таким образом мы вычислили начальное смещение функции. Теперь к начальному смещению добавляем смещение где у нас программа упала. Все вычисления в шестнадцатиричной форме.

    1. 18 + 2A = 42
    2. 28 + 16 = 3E

    Теперь в листинге осталось найти строку, в которой все упало. Для этого спускаемся вниз по функции до момента, пока не увидим полученное смещение. При этом нужно не выходить за рамки самой функции. Если вышли за рамки, то на это может быть несколько причин:
    1. Исходник отличается от того, который был при сборке программы.
    2. Где-то в расчетах смещения мы ошиблись и посчитали неправильно.
    3. Что же мы вообще считали.

    1.
     000034  5020  50C0        000003 |                 ST    r2,b(,r5,192)
                               000004 |       *  {
                               000005 |       *  .char *value = 0L;
     000038  4110  0000        000005 |                 LA    r1,0
     00003C  1821              000005 |                 LR    r2,r1
     00003E  5020  50C4        000005 |                 ST    r2,value(,r5,196)
                               000006 |       *  .value[0] = '\0';
     000042  9200  2000        000006 |                 MVI   (char)(r2,0),0
                               000007 |       *  .return(0);
                               000008 |       *  }
     000046                    000008 |        @1L6     DS    0H
    

    2.
     000030  5020  4844        000003 |                 ST    r2,b(,r4,2116)
                               000004 |       *  {
                               000005 |       *  .char *value = 0L;
     000034  4130  0000        000005 |                 LA    r3,0
     000038  1813              000005 |                 LR    r1,r3
     00003A  5010  47E0        000005 |                 ST    r1,value(,r4,2016)
                               000006 |       *  .value[0] = '\0';
     00003E  9200  1000        000006 |                 MVI   (char)(r1,0),0
                               000007 |       *  .return(0);
                               000008 |       *  }
     000042                    000008 |        @1L6     DS    0H
    

    В обоих случаях мы получили ту же самую строку номер 6. Не обращая внимания на ассемблер, смотрим номер строки в 3-м столбце, 000006. Если есть желание, то можно продолжить искать ошибку по этому листингу, но я бы пошел в исходники.

    Дальше я не буду приводить 2 версии без XPLINK и с XPLINK, я ограничусь первой.

    Если у нас не оказалось stderr вывода с информацией, которая была выше (я имею ввиду вывод сообщения об ошибке), то это же можно найти в CEE дамп файле. Для этого открываем СЕЕ дамп и ищем самую первую таблицу.

      Traceback:
        DSA Addr  Program Unit  PU Addr   PU Offset  Entry         E Addr    E  Offset   Statement  Load Mod  Service  Status
        10020CF0  CEEHDSP       046C0B00  +000048DA  CEEHDSP       046C0B00  +000048DA              CEEPLPKA  UK10749  Call
        100202B0                1000A930  +0000002A  f1(int,char*)
                                                                   1000A930  +0000002A              *PATHNAM           Exception
        10020210                1000A968  +0000006E  main          1000A968  +0000006E              *PATHNAM           Call
        100200F8                044EFCB6  +000000B4  EDCZMINV      044EFCB6  +000000B4              CEEEV003           Call
        10020030  CEEBBEXT      046C69E8  +000001A6  CEEBBEXT      046C69E8  +000001A6              CEEPLPKA  HLE7709  Call
    

    В этой таблице мы ищем Exception в последней строке, это и есть наша функция, где программа упала, в столбце Enrty название функции, в столбце E Offset — смещение по которому программа упала.

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

    Что делать, если у нас по коду не видно, где ошибка.

    Для этого в CEE дампе смотрим, ассемблерный код (Описание ассемблерных инструкций для z/OS можно найти в Principal of Operation, глава 7).

                               000006 |       *  .value[0] = '\0';
     000042  9200  2000        000006 |                 MVI   (char)(r2,0),0
    

    Как видно, данная инструкция пытается записать 0 по адресу (r2+0). Вместо нуля тут может быть какое угодно другое смещение, но в данном случае 0. Встает вопрос, а что же у нас в регистре r2.

    Для этого в CEE дампе есть распечатки регистров и участков памяти, на которые эти регистры указывают.

      Condition Information for Active Routines
        Condition Information for  (DSA address 100202B0)
          CIB Address: 10021630
          Current Condition:
            CEE3204S The system detected a protection exception (System Completion Code=0C4).
          Location:
            Program Unit:  Entry: f1(int,char*)
            Statement:     Offset: +0000002A
          Machine State:
            ILC..... 0004    Interruption Code..... 0004
            PSW..... 078D1400 9000A95E
            GPR0..... 100087E8  GPR1..... 00000000  GPR2..... 00000000  GPR3..... 00000001
            GPR4..... 1000A9A8  GPR5..... 100202B0  GPR6..... 1000AAAC  GPR7..... 1000A0F0
            GPR8..... 00000030  GPR9..... 80000000  GPR10.... 844EFCAA  GPR11.... 846C69E8
            GPR12.... 1001A7D0  GPR13.... 100202B0  GPR14.... 9000A9D8  GPR15.... 1000A930
            FPC...... 00000000
            FPR0..... 4DBE5D7C  A198209F            FPR1..... 00000000  00000000
            FPR2..... 00000000  00000000            FPR3..... 00000000  00000000
            FPR4..... 00000000  00000000            FPR5..... 00000000  00000000
            FPR6..... 00000000  00000000            FPR7..... 00000000  00000000
            FPR8..... 00000000  00000000            FPR9..... 00000000  00000000
            FPR10.... 00000000  00000000            FPR11.... 00000000  00000000
            FPR12.... 00000000  00000000            FPR13.... 00000000  00000000
            FPR14.... 00000000  00000000            FPR15.... 00000000  00000000
        Storage dump near condition, beginning at location: 1000A94A
          +000000 1000A94A  50BC5020 50C04110 00001821 502050C4  92002000 5850D028 47F0E004 070747F0  |&.&.&.......&.&Dk....&...0.....0|
    

    или еще можно найти дальше

        f1(int,char*) (DSA address 100202B0):
          UPSTACK DSA
          Saved Registers:
            GPR0..... 100087E8  GPR1..... 00000000  GPR2..... 00000000  GPR3..... 00000001
            GPR4..... 1000A9A8  GPR5..... 100202B0  GPR6..... 1000AAAC  GPR7..... 1000A0F0
            GPR8..... 00000030  GPR9..... 80000000  GPR10.... 844EFCAA  GPR11.... 846C69E8
            GPR12.... 1001A7D0  GPR13.... 100202B0  GPR14.... 9000A9D8  GPR15.... 1000A930
        GPREG STORAGE:
          Storage around GPR0 (100087E8)
            -0020 100087C8  00000000 00000000 00000000 00000000  00000000 00000000 00000000 00000000  |................................|
            +0000 100087E8  1001C028 00000000 00000000 00000000  10017728 10017732 00000000 00000000  |................................|
            +0020 10008808  00000000 00000000 00000000 00000000  00000001 00000000 00000001 00000000  |................................|
          Storage around GPR1 (00000000)
            +0000 00000000    Inaccessible storage.
            +0020 00000020    Inaccessible storage.
            +0040 00000040    Inaccessible storage.
          Storage around GPR2 (00000000)
            +0000 00000000    Inaccessible storage.
            +0020 00000020    Inaccessible storage.
            +0040 00000040    Inaccessible storage.
          Storage around GPR3 (00000001)
            -0001 00000000    Inaccessible storage.
            +001F 00000020    Inaccessible storage.
            +003F 00000040    Inaccessible storage.
          Storage around GPR4 (1000A9A8)
            -0020 1000A988  05404140 401E07F4 90E5D00C 58E0D04C  4100E0A0 5500C314 4140F040 4720F014  |. .  ..4.V.....<......C.. 0 ..0.|
            +0000 1000A9A8  5000E04C 9210E000 50D0E004 18DE5800  C1F45000 D098C050 00000021 5800D098  |&..<k...&.......A4&..q.&.......q|
            +0020 1000A9C8  58F04050 41300001 18131825 4DE0F010  47000008 18F35800 D0985000 C1F4180D  |.0 &........(.0......3...q&.A4..|
    

    Обратите внимание, что бы распечатка регистров относилась к данной функции.

    Посмотрев на значения регистров, мы получаем r2=0 и следовательно (0+0). Теперь все стало ясно, программа пытается записать данные по адресу 0, который является недоступным ни для чтения, ни для записи.

    Все, теперь осталось найти в исходном коде, почему это происходит, почему value указывает на 0, но это уже другая тема.

    Литература: z/Architecture Principles of Operation