СТАТЬЯ
27.04.01

Предыдущая часть
Средства тестирования от компании Rational

c Александр Новичков,
Компьютер-пресс #2 2001

Cтатья была опубликована на сайте www.compress.ru

Сообщения об ошибках и предупреждениях.

Настройка фильтра.

Rational Purify по своей природе способна выловить не только ошибки, связанные с потерей памяти, но также ряд других не менее важных ошибок. Следует определить, что все сообщения делятся на две категории: ОШИБКИ и ПРЕДУПРЕЖДЕНИЯ. Во время запуска программа будет дотошно собирать все виды сообщений, и только настройка фильтра позволит отказаться от заведомо лишней, ненужной информации. Система фильтров Purify способна настроить не только уровень “придирчивости” к программе, но и количество исследуемых внешних модулей (чтобы разработчик мог концентрироваться только на собственных ошибках и не огорчался по поводу системных ошибок).

По умолчанию Purify выводит все сообщения и предупреждения, что может повергнуть разработчика в шок (даже в абсолютно правильной программе могут быть определенные предупреждения). Это связанно со спецификой поиска неточностей, так как некоторые предупреждения могут счиаться ошибкой, а могут и не быть таковыми - все зависит от конкретного алгоритма! Вот почему Purify и предлагает мощный фильтр

Как видно из рисунка, предполагается ставить фильтры либо по сообщениям (вручную) либо по категориям (при этом соответствующие сообщения выберутся автоматически). Обратите внимание на список сообщений… количество доходит до 41 и растет с каждой новой версией! Перед тем, как мы перейдем к рассмотрению всех сообщений, хочется отметить очень важный нюанс: Purify способен работать совместно с отладчиком (он прописывается отдельно). В этом случае возможна двойная работа по отладке программы с установкой брейкпоинтов…. итд.

Попробуем рассмотреть некоторые сообщения Purify c комментариями и примерами:

ABR: Array Bounds Read Выход за пределы массива при чтении

 
#include <iostream.h>
#include <windows.h>         
int main(int, char **)          
{        
    int *ptr = new int[2];          //Определить число элементов          
    ptr[0] = 0;          
    ptr[1] = 1;          
    for (int i=0; i <= 2; i++) {    //ОШИБКА          
        //ABR when i is 2          
        cerr << "ptr[" << i << "] == " << ptr[i] << '\n';         
    }         
    delete[] ptr;         
    return(0);       
}       
ABW: Array Bounds Write Выход за пределы массива при записи
   #include <iostream.h> 
        #include <windows.h> 
        int main(int, char **) 
        { 
            int *ptr = new int[2];          //Определить число элементов 
            for (int i=0; i <= 2; i++) {    //ОШИБКА 
                ptr[i] = i; 
                cerr << "ptr[" << i << "] == " << ptr[i] << '\n'; //ABW + ABR when i is 2 
            } 
            delete[] ptr; 
            return(0); 
        }
   
ABWL: Late Detect Array Bounds Write Cообщение указывает, что программа записала значение перед началом или после конца распределенного блока памяти
        #include <iostream.h> 
        #include <windows.h> 
        int main(int, char **) 
        { 
            int *ptr = new int[2];          //Определить число элементов 
            for (int i=0; i <= 2; i++) {    //ОШИБКА 
                ptr[i] = i; 
                cerr << "ptr[" << i << "] == " << ptr[i] << '\n'; 
            } 
            delete[] ptr; //ABWL: ОШИБКА 
            return(0); 
        }
BSR: Beyond Stack Read сообщение указывает, что функция в программе собирается читать вне текущего указателя вершины стека
        #include <windows.h> 
        #include <iostream.h> 
        #define A_LOT 256 
        int * setup_values(void) 
        { 
            int values[A_LOT];    //ОШИБКА: должен быть статичным 
            for (int i=0; i < A_LOT; i++) { 
                values[i] = i; 
            } 
            return(values);       //ОШИБКА: неизвестно, что возвращать 
        } 
        int main(int, char **) 
        { 
            int *values; 
            values = setup_values(); 
            for (int i=0; i < A_LOT; i++) { 
                //BSR: значения из "setup_values" больше не находятся в стеке 
                cerr << "element #" << i << " is " << values[i] << '\n'; 
            } 
            return(0); 
        }
  
BSW: Beyond Stack Write сообщение указывает, что функция в программе собирается писать вне текущего указателя вершины стека (пример см. выше)

FFM: Freeing Freed Memory Попытка освобождения свободного блока памяти

        #include <iostream.h> 
        #include <windows.h> 
        int 
        main(int, char **) 
        { 
            int *ptr1 = new int;    // int 
            int *ptr2 = ptr1;       //ОШИБКА: должен дублировать объект, а не копировать указатель 
            *ptr1 = 10; 
            *ptr2 = 20; 
            cerr << "ptr1" << " is " << *ptr1 << '\n'; 
            cerr << "ptr2" << " is " << *ptr2 << '\n'; 
            delete ptr1; 
            delete ptr2;            //FFM: ОШИБКА 
            return(0); 
        }
 
FIM: Freeing Invalid Memory Попытка освобождения некорректного блока памяти
        #include <iostream.h> 
        int main(int, char **) 
        { 
           int i; 
           delete[] &i;    //FIM: не было операции new. Освобождать нечего! 
           return(0); 
        }
 
FMM: Freeing Mismatched Memory Сообщение указывает, что программа пробует освобождать память с неправильным ВЫЗОВОМ API для того типа памяти
        #include <windows.h> 
        int main(int, char **) 
        { 
            HANDLE heap1, heap2; 
            heap1 = HeapCreate(0, 1000, 0); 
            heap2 = HeapCreate(0, 1000, 0); 
            int *pointer = (int *) HeapAlloc(heap1, 0, sizeof(int)); 
            HeapFree(heap2, 0, pointer);   //ОШИБКА: неправильное освобождение памяти 
            HeapDestroy(heap1); 
            HeapDestroy(heap2); 
            return(0); 
        }
      
FMR: Free Memory Read Попытка чтения уже освобожденного блока памяти
        #include <iostream.h> 
        #include <windows.h> 
        int main(int, char **) 
        { 
            int *ptr = new int[2]; 
            ptr[0] = 0; 
            ptr[1] = 1; 
            delete[] ptr;   //ОШИБКА: (специально сделано удаление) 
            for (int i=0; i < 2; i++) { 
                //FMR: ОШИБКА ДОСТУПА К ПАМЯТИ 
                cerr << "element #" << i << " is " << ptr[i] << '\n'; 
            } 
            return(0); 
        }
  
FMW: Free Memory Write Попытка записи уже освобожденного блока памяти
        #include <iostream.h> 
        #include <windows.h> 
        int main(int, char **) 
        { 
            int *ptr = new int[2]; 
            ptr[0] = 0; 
            ptr[1] = 1; 
            delete[] ptr;   //ОШИБКА: (специально сделано удаление) 
            for (int i=0; i < 2; i++) { 
              ptr[i] *= i; //FMR + FMW: потому что ptr уже удален 
              cerr << "element #" << i << " is " << ptr[i] << '\n'; //FMR 
            } 
            return(0); 
        }
 
HAN: Invalid Handle Операции над неправильным дескриптором
        #include <iostream.h> 
        #include <windows.h> 
        #include <malloc.h> 
        int main(int, char **) 
        { 
            (void) LocalUnlock((HLOCAL)3);//HAN: 3 – неправильный указатель 
            return(0); 
        }
 
HIU: Handle In Use Индикация утечки ресурсов. Неправильная индикация дескриптора
        #include <iostream.h> 
        #include <windows.h> 
        static long 
        get_alignment(void) 
        { 
            SYSTEM_INFO sys_info; 
            GetSystemInfo(&sys_info); 
            return(sys_info.dwAllocationGranularity); 
        } 
        int 
        main(int, char **) 
        { 
            const long align = get_alignment(); 
            HANDLE      file_handle = CreateFile("file.txt", 
                                        
        GENERIC_READ|GENERIC_WRITE, 0, NULL, 
                                        
        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 
            if (file_handle == INVALID_HANDLE_VALUE) { 
                cerr << "ОШИБКА файла\n"; 
                return(1); 
            } 
            HANDLE      map_handle = CreateFileMapping(file_handle, NULL, 
                                        
        PAGE_READWRITE, 0, align, "mymap"); 
            if (map_handle == INVALID_HANDLE_VALUE) { 
                cerr << "Unable to create actual mapping\n"; 
                return(1); 
            } 
            char       *pointer = (char *) MapViewOfFile(map_handle, FILE_MAP_WRITE, 
                                                
          0, 0, align); 
            if (pointer == NULL) { 
                cerr << "Unable to map into address space\n"; 
                return(1); 
            } 
            strcpy(pointer, "hello\n"); 
            //HIU: map_handle все еще доступный и правильный 
            //HIU: file_handle все еще доступный и правильный 
            return(0); 
        }
 
ILK: COM Interface Leak Утечка COM интерфейса
        #include <windows.h> 
        int main(int, char **) 
        { 
            LPMALLOC        lpIm; 
            CoGetMalloc( MEMCTX_TASK, (LPMALLOC*)&lpIm); 
        IMalloc_Release(lpIm); //НЕВЕРНЫЙ ЗАПРОС 
            return(0); 
        }
 
IPR: Invalid Pointer Read Ошибка обращения к памяти, когда программа пытается произвести чтение из недоступной области
       #include <iostream.h> 
       #include <windows.h> 
       int main(int, char **) 
       { 
           int *ptr = (int *) 0x80000000; //ОШИБКА: Указатель на зарезервированную часть адресного пространства 
           for (int i=0; i < 2; i++) { 
               //IPR: Попытка обращения к недопустимому указателю 
               cerr << "ptr[" << i << "] == " << ptr[i] << '\n'; 
           } 
           return(0); 
       }
 
IPW: Invalid Pointer Write Ошибка обращения к памяти, когда программа пытается произвести запись из недоступной области
        #include <iostream.h> 
        #include <windows.h> 
        int main(int, char **) 
        { 
            int *ptr = (int *) 0x80000000; //ОШИБКА: Указатель на зарезервированную часть адресного пространства 
            for (int i=0; i < 2; i++) { 
                //IPW + IPR: Попытка обращения к недопустимому указателю 
                ptr[i] = i; 
                cerr << "ptr[" << i << "] == " << ptr[i] << '\n'; 
            } 
            return(0); 
        }
      
MAF: Memory Allocation Failure Ошибка в запросе на распределение памяти
        #include <iostream.h> 
        #include <windows.h> 
        #define VERY_LARGE 3000000000       //Больше, чем можем получить 
        int main(int, char **) 
        { 
            int *ptr = new int[VERY_LARGE / sizeof(int)];   //MAF: нельзя так много 
            if (ptr == 0) { 
                cerr << "Failed to alloc, as expected\n"; 
        return (1); 
            } else { 
                cerr << "Got " << VERY_LARGE << " bytes @" << (unsigned long)ptr << 
          '\n'; 
        delete[] ptr; 
            return(0); 
        } 
        }
      
MLK: Memory Leak Утечка памяти
        #include <windows.h> 
        #include <iostream.h> 
        int main(int, char **) 
        { 
            (void) new char[1000]; 
            (void) new char[1000]; 
            (void) new char[1000]; 
            (void) new char[1000]; 
            (void) new char[1000]; 
            //5 килобайт потерь 
            return(0); 
        } 
        ИЛИ 
        void All::OnAppAbout() 
        { 
        char *alex; //Указатель 
        alex=(char *)malloc(20000); //MLK: берем, но не отдаем 
        CAboutDlg aboutDlg; 
        aboutDlg.DoModal(); 
        } 
MPK: Potential Memory Leak Потенциальная утечка памяти (возникает когда производится операция над массивом не с нулевого элемента)
        #include <iostream.h> 
        #include <windows.h> 
        int main(int, char **) 
        { 
            static char *ptr = new char[500000]; 
            ptr += 100;   //MPK: обнаружится, как потенциально пропущенное 
            return(0); 
        }
      
NPR: Null Pointer Read Попытка чтения с нулевого адреса
        #include <iostream.h> 
        #include <windows.h> 
        int 
        main(int, char **) 
        { 
            int *ptr = (int *) 0x0; //ОШИБКА 
            for (int i=0; i < 2; i++) { 
                //NPR: ошибка доступа 
                cerr << "ptr[" << i << "] == " << ptr[i] << '\n'; 
            } 
            return(0); 
        }
      
NPW: Null Pointer Write Попытка записи в нулевой адрес
        #include <iostream.h> 
        #include <windows.h> 
        int 
        main(int, char **) 
        { 
            int *ptr = (int *) 0x0; //ОШИБКА 
            for (int i=0; i < 2; i++) { 
                //NPW: ошибка доступа 
                ptr[i] = i; 
                cerr << "ptr[" << i << "] == " << ptr[i] << '\n'; 
            } 
            return(0); 
        }
      
UMC: Uninitialized Memory Copy Попытка копирования непроинициализированного блока памяти
        #include <iostream.h> 
        #include <windows.h> 
        #include <string.h> 
        int main(int, char **) 
        { 
            char *ptr = new char[10]; 
            char var[10]; 
        memcpy(var, ptr, 10); //UMC предупреждение 
        delete[] ptr; 
            return(0); 
        }
      
UMR: Uninitialized Memory Read Попытка чтения непроинициализированного блока памяти
        #include <iostream.h> 
        #include <windows.h> 
        int 
        main(int, char **) 
        { 
            int *ptr = new int; 
            cerr << "*ptr is " << *ptr << '\n';     //UMR: нет значения в ptr 
            delete[] ptr; 
            return(0); 
        } 
      
Подведем итоги статьи описанием основных возможностей Purify:
  1. Отслеживание ошибок доступа к памяти
  2. Сбор и вывод статистики по использованию памяти
  3. Использование комплексного подхода к тщательному тестированию
  4. Технология OCI – Object Code Insertion позволяет детально отследить и выловить ошибку не только в контролируемом модуле, но и в модулях DLL сторонних разработчиков
  5. Тестирование ActiveX, COM/DCOM, ODBC, DLL
  6. Настраиваемый, двухуровневый способ тестирования (придирчивости) приложений
  7. Интеграция с Visual Studio
  8. Открытое API. Позволяет дописывать разработчикам собственные модули и присоединять их.
  9. Совместная работа с любым отладчиком
  10. Тестирование системных вызовов

Дополнительная информация

Средства тестирования Rational (VisualTest, Purify, PureCoverage, Quantify)

Дополнительную информацию Вы можете получить в компании Interface Ltd.

Обсудить на форуме Rational Software
Отправить ссылку на страницу по e-mail


Interface Ltd.

Ваши замечания и предложения отправляйте автору
По техническим вопросам обращайтесь к вебмастеру
Документ опубликован: 27.04.01